Index: openafs/src/WINNT/afsd/afsd_flushvol.c
diff -c openafs/src/WINNT/afsd/afsd_flushvol.c:1.6 openafs/src/WINNT/afsd/afsd_flushvol.c:1.6.2.1
*** openafs/src/WINNT/afsd/afsd_flushvol.c:1.6	Fri Jun 18 00:52:25 2004
--- openafs/src/WINNT/afsd/afsd_flushvol.c	Sun Oct  3 09:35:15 2004
***************
*** 27,32 ****
--- 27,35 ----
  
  #include "afsd_flushvol.h"
  #include "afsd_eventlog.h"
+ #include "lanahelper.h"
+ 
+ extern void afsi_log(char *pattern, ...);
  
  static FLUSHVOLTHREADINFO	gThreadInfo   = {0};
  static HANDLE			gThreadHandle = NULL;
***************
*** 45,189 ****
  afs_int32
  afsd_ServicePerformFlushVolumeCmd(char *data)
  {
! 	register afs_int32 code;
! 	struct ViceIoctl blob;
  
! 	memset(&blob, '\0', sizeof(blob));
! 	code = pioctl(data, VIOC_FLUSHVOLUME, &blob, 0);
      
! 	return code;
  }
  
  BOOL
  afsd_ServicePerformFlushVolumes()
! {
! 	CONST CHAR	COLON = ':';
! 	CONST CHAR	SLASH = '\\';
! 	CONST DWORD	NETRESBUFSIZE = 16384;
! 	CHAR		bufMessage[1024];
! 	UINT		i;
! 	DWORD		dwServerSize;
! 	DWORD		dwRet;
! 	DWORD		dwCount;
! 	DWORD		dwNetResBufSize;
! 	DWORD		dwTotalVols = 0;
! 	DWORD		dwVolBegin, dwVolEnd;
! 	DWORD		dwFlushBegin, dwFlushEnd;
! 	HANDLE		hEnum;
! 	LPNETRESOURCE	lpNetResBuf, lpnr;
! 	PCHAR		pszShareName, pc;
! 	afs_int32	afsRet = 0;
! 	
! 	// Determine the root share name (\\AFS\ALL or \\<machine>-AFS\ALL),
! 	// and the length of the server name prefix.
! 	pszShareName = smb_GetSharename();
! 	if (pszShareName == NULL)
! 	{
! 		LogEvent(EVENTLOG_ERROR_TYPE, MSG_FLUSH_NO_SHARE_NAME, NULL);
! 		return FALSE;
! 	}
! 	pc = strrchr(pszShareName, SLASH);
! 	if ((pc == NULL) || ((dwServerSize = pc - pszShareName) < 3))
! 	{
! 		LogEvent(EVENTLOG_ERROR_TYPE, MSG_FLUSH_BAD_SHARE_NAME,
! 			 pszShareName, NULL);
! 		free(pszShareName);
! 		return FALSE;
! 	}
! 
! 	// Allocate a buffer to hold network resources returned by
! 	// WNetEnumResource().
! 	lpNetResBuf = malloc(NETRESBUFSIZE);
! 	if (lpNetResBuf == NULL)
! 	{
! 		// Out of memory, give up now.
! 		LogEvent(EVENTLOG_ERROR_TYPE, MSG_FLUSH_NO_MEMORY, NULL);
! 		free(pszShareName);
! 		return FALSE;
! 	}
! 
! 	// Initialize the flush timer.  Note that GetTickCount() returns
! 	// the number of milliseconds since the system started, in a DWORD,
! 	// so that the value wraps around every 49.7 days.  We do not bother
! 	// to handle the case where the flush elapsed time is greater than
! 	// that.
! 	dwFlushBegin = GetTickCount();
! 	
! 	dwRet = WNetOpenEnum(RESOURCE_CONNECTED, RESOURCETYPE_ANY, 0, NULL,
! 			     &hEnum);
! 	if (dwRet != NO_ERROR)
! 	{
! 		LogEventMessage(EVENTLOG_ERROR_TYPE, MSG_FLUSH_OPEN_ENUM_ERROR,
! 				dwRet);
! 		free(pszShareName);
! 		return FALSE;
! 	}
! 
! 	// Loop to enumerate network resources, and flush those associated
! 	// with AFS volumes.
! 	while (1)
! 	{
! 		dwCount = -1;
! 		memset(lpNetResBuf, 0, NETRESBUFSIZE);
! 		dwNetResBufSize = NETRESBUFSIZE;
! 		dwRet = WNetEnumResource(hEnum, &dwCount,
! 					 lpNetResBuf, &dwNetResBufSize);
! 		if (dwRet != NO_ERROR)
! 			break;
! 		// Iterate over the returned network resources.
! 		for (i = 0, lpnr = lpNetResBuf; i < dwCount; i++, lpnr++)
! 		{
! 			// Ensure resource has a remote name, and is connected.
! 			if ((lpnr->lpRemoteName == NULL) ||
! 			    (lpnr->dwScope != RESOURCE_CONNECTED))
! 				continue;
! 			if ((_strnicmp(lpnr->lpRemoteName, pszShareName,
! 				       dwServerSize) == 0) &&
! 			    (lpnr->lpRemoteName[dwServerSize] == SLASH))
! 			{
! 				// got one!
! 				// but we don't want to flush '\\[...]afs\all'
! 				if (_stricmp(lpnr->lpRemoteName,
! 					     pszShareName) == 0)
! 					continue;
! 				++dwTotalVols;
! 
! 				dwVolBegin = GetTickCount();
! 				afsRet = afsd_ServicePerformFlushVolumeCmd(lpnr->lpRemoteName);
! 				dwVolEnd = GetTickCount();
! 				if (afsRet == 0)
! 				{
! 					LogTimingEvent(MSG_TIME_FLUSH_PER_VOLUME,
! 						       lpnr->lpRemoteName,
! 						       dwVolEnd - dwVolBegin);
! 				}
! 				else
! 				{
! 					LogEvent(EVENTLOG_WARNING_TYPE,
! 						 MSG_FLUSH_FAILED,
! 						 lpnr->lpRemoteName, NULL);
! 				}
! 			}
! 		}
! 	}
! 	WNetCloseEnum(hEnum);
! 	free(lpNetResBuf);
! 	free(pszShareName);
! 	if (dwRet != ERROR_NO_MORE_ITEMS)
! 	{
! 		LogEventMessage(EVENTLOG_ERROR_TYPE, MSG_FLUSH_ENUM_ERROR,
! 				dwRet);
! 		return FALSE;
! 	}
  
! 	dwFlushEnd = GetTickCount();
  	
! 	// display total volume count in Event Logger
! 	sprintf(bufMessage, "%d", dwTotalVols);
! 	LogTimingEvent(MSG_TIME_FLUSH_TOTAL, bufMessage,
! 		       dwFlushEnd - dwFlushBegin);
  
! 	return TRUE;
  }
  
  // Report a timing event to the system event log.
--- 48,197 ----
  afs_int32
  afsd_ServicePerformFlushVolumeCmd(char *data)
  {
!     register afs_int32 code;
!     struct ViceIoctl blob;
  
!     afsi_log("Flushing Volume \"%s\"",data);
!     memset(&blob, '\0', sizeof(blob));
!     code = pioctl(data, VIOC_FLUSHVOLUME, &blob, 0);
      
!     return code;
  }
  
  BOOL
  afsd_ServicePerformFlushVolumes()
! {       
!     CONST CHAR	COLON = ':';
!     CONST CHAR	SLASH = '\\';
!     CONST DWORD	NETRESBUFSIZE = 16384;
!     CHAR		bufMessage[1024];
!     UINT		i;
!     DWORD		dwServerSize;
!     DWORD		dwRet;
!     DWORD		dwCount;
!     DWORD		dwNetResBufSize;
!     DWORD		dwTotalVols = 0;
!     DWORD		dwVolBegin, dwVolEnd;
!     DWORD		dwFlushBegin, dwFlushEnd;
!     HANDLE		hEnum;
!     LPNETRESOURCE	lpNetResBuf, lpnr;
!     PCHAR		pszShareName, pc;
!     afs_int32	afsRet = 0;
! 
!     if ( lana_OnlyLoopback() ) {
!         // Nothing to do if we only have a loopback interface
!         return TRUE;
!     }
! 
!     // Determine the root share name (\\AFS\ALL or \\<machine>-AFS\ALL),
!     // and the length of the server name prefix.
!     pszShareName = smb_GetSharename();
!     if (pszShareName == NULL)
!     {
!         LogEvent(EVENTLOG_ERROR_TYPE, MSG_FLUSH_NO_SHARE_NAME, NULL);
!         return FALSE;
!     }
!     pc = strrchr(pszShareName, SLASH);
!     if ((pc == NULL) || ((dwServerSize = pc - pszShareName) < 3))
!     {
!         LogEvent(EVENTLOG_ERROR_TYPE, MSG_FLUSH_BAD_SHARE_NAME,
!                   pszShareName, NULL);
!         free(pszShareName);
!         return FALSE;
!     }
! 
!     // Allocate a buffer to hold network resources returned by
!     // WNetEnumResource().
!     lpNetResBuf = malloc(NETRESBUFSIZE);
!     if (lpNetResBuf == NULL)
!     {
!         // Out of memory, give up now.
!         LogEvent(EVENTLOG_ERROR_TYPE, MSG_FLUSH_NO_MEMORY, NULL);
!         free(pszShareName);
!         return FALSE;
!     }
! 
!     // Initialize the flush timer.  Note that GetTickCount() returns
!     // the number of milliseconds since the system started, in a DWORD,
!     // so that the value wraps around every 49.7 days.  We do not bother
!     // to handle the case where the flush elapsed time is greater than
!     // that.
!     dwFlushBegin = GetTickCount();
! 
!     dwRet = WNetOpenEnum(RESOURCE_CONNECTED, RESOURCETYPE_ANY, 0, NULL,
!                           &hEnum);
!     if (dwRet != NO_ERROR)
!     {
!         LogEventMessage(EVENTLOG_ERROR_TYPE, MSG_FLUSH_OPEN_ENUM_ERROR,
!                          dwRet);
!         free(pszShareName);
!         return FALSE;
!     }
! 
!     // Loop to enumerate network resources, and flush those associated
!     // with AFS volumes.
!     while (1)
!     {
!         dwCount = -1;
!         memset(lpNetResBuf, 0, NETRESBUFSIZE);
!         dwNetResBufSize = NETRESBUFSIZE;
!         dwRet = WNetEnumResource(hEnum, &dwCount,
!                                   lpNetResBuf, &dwNetResBufSize);
!         if (dwRet != NO_ERROR)
!             break;
!         // Iterate over the returned network resources.
!         for (i = 0, lpnr = lpNetResBuf; i < dwCount; i++, lpnr++)
!         {
!             // Ensure resource has a remote name, and is connected.
!             if ((lpnr->lpRemoteName == NULL) ||
!                  (lpnr->dwScope != RESOURCE_CONNECTED))
!                 continue;
!             if ((_strnicmp(lpnr->lpRemoteName, pszShareName,
!                             dwServerSize) == 0) &&
!                  (lpnr->lpRemoteName[dwServerSize] == SLASH))
!             {
!                 // got one!
!                 // but we don't want to flush '\\[...]afs\all'
!                 if (_stricmp(lpnr->lpRemoteName, pszShareName) == 0)
!                     continue;
!                 ++dwTotalVols;
! 
!                 dwVolBegin = GetTickCount();
!                 afsRet = afsd_ServicePerformFlushVolumeCmd(lpnr->lpRemoteName);
!                 dwVolEnd = GetTickCount();
!                 if (afsRet == 0)
!                 {
!                     LogTimingEvent(MSG_TIME_FLUSH_PER_VOLUME,
!                                     lpnr->lpRemoteName,
!                                     dwVolEnd - dwVolBegin);
!                 }
!                 else
!                 {
!                     LogEvent(EVENTLOG_WARNING_TYPE,
!                               MSG_FLUSH_FAILED,
!                               lpnr->lpRemoteName, NULL);
!                 }
!             }
!         }
!     }
!     WNetCloseEnum(hEnum);
!     free(lpNetResBuf);
!     free(pszShareName);
!     if (dwRet != ERROR_NO_MORE_ITEMS)
!     {
!         LogEventMessage(EVENTLOG_ERROR_TYPE, MSG_FLUSH_ENUM_ERROR,
!                          dwRet);
!         return FALSE;
!     }
  
!     dwFlushEnd = GetTickCount();
  	
!     // display total volume count in Event Logger
!     sprintf(bufMessage, "%d", dwTotalVols);
!     LogTimingEvent(MSG_TIME_FLUSH_TOTAL, bufMessage,
!                     dwFlushEnd - dwFlushBegin);
  
!     return TRUE;
  }
  
  // Report a timing event to the system event log.
***************
*** 193,203 ****
  static VOID
  LogTimingEvent(DWORD dwEventID, LPTSTR lpString1, DWORD dwTime)
  {
! 	CHAR	szTime[16];
  	
! 	sprintf(szTime, "%lu", dwTime);
! 	LogEvent(EVENTLOG_INFORMATION_TYPE, dwEventID, lpString1, szTime,
! 		 NULL);
  }
  
  
--- 201,211 ----
  static VOID
  LogTimingEvent(DWORD dwEventID, LPTSTR lpString1, DWORD dwTime)
  {
!     CHAR	szTime[16];
  	
!     sprintf(szTime, "%lu", dwTime);
!     LogEvent(EVENTLOG_INFORMATION_TYPE, dwEventID, lpString1, szTime,
!               NULL);
  }
  
  
***************
*** 223,313 ****
  //
  HANDLE GetUserToken(DWORD access)
  {
! 	HANDLE hTok = NULL;
! 	DWORD pid = 0, tid = 0;
! 
! 	// Try it the easy way first - look for a window owned by the shell on
! 	// our current desktop.  If we find one, use that to get the process id.
! 	HWND shell = FindWindowEx(NULL, NULL, "Progman", NULL);
! 	if (shell != NULL)
! 	{
! 		tid = GetWindowThreadProcessId(shell, &pid);
! 	}
! 
! 	// We are possibly running on a private window station and desktop: we must
! 	// switch to the default (which we suppose is where we will find the
! 	// running shell).
! 	else
! 	{
! 		HWINSTA saveWinSta = GetProcessWindowStation(); 
! 		HDESK saveDesk = GetThreadDesktop(GetCurrentThreadId()); 
! 		HWINSTA winSta = NULL;
! 		HDESK desk = NULL;
! 		BOOL changeFlag = FALSE;
! 		BOOL dummy = saveWinSta != NULL &&
! 					 saveDesk != NULL &&
! 					 (winSta = OpenWindowStation("WinSta0", FALSE,
! 									MAXIMUM_ALLOWED)) != NULL &&
! 					 (changeFlag = SetProcessWindowStation(winSta)) != 0 &&
! 					 (desk = OpenDesktop("Default", 0, FALSE,
! 									MAXIMUM_ALLOWED)) != NULL &&
! 					 SetThreadDesktop(desk) != 0;
! 
! 		// Now find the window and process on this desktop
! 		shell = FindWindowEx(NULL, NULL, "Progman", NULL);
! 		if (shell != NULL) 
! 		{
! 			tid = GetWindowThreadProcessId(shell, &pid);
! 		}
! 
! 		// Restore our own window station and desktop
! 		if (changeFlag)
! 		{
! 			SetProcessWindowStation(saveWinSta);
! 			SetThreadDesktop(saveDesk);
! 		}
! 
! 		// Close temporary objects
! 		if (winSta != NULL)
! 			CloseWindowStation(winSta);
! 		if (desk != NULL) 
! 			CloseDesktop(desk);
! 	}
! 
! 	//
! 	// If we have a process id, use that to get the process handle and 
! 	// from there the process' access token.
! 	//
! 	if (pid != 0)
! 	{
! 		HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
! 		if (hProc != NULL)
! 		{
! 			OpenProcessToken(hProc, access, &hTok) || (hTok = NULL);
! 			CloseHandle(hProc);
! 		}
! 	}
  
! 	// Return token if we got one
! 	return hTok;
! }
  
  // impersonate logged-on user as client
  BOOL
  ImpersonateClient()
  {
! 	DWORD	dwDesiredAccess = TOKEN_ALL_ACCESS;
! 	HANDLE	hUserToken = GetUserToken(dwDesiredAccess);
  	
! 	if (hUserToken == NULL)
! 		return FALSE;
! 	if (ImpersonateLoggedOnUser(hUserToken) == 0)
! 	{
! 		LogEvent(EVENTLOG_ERROR_TYPE, MSG_FLUSH_IMPERSONATE_ERROR,
! 			 NULL);
! 		return FALSE;
! 	}
! 	return TRUE;
  }
  	
  /////////////////////////////////////////////////////////////////////
--- 231,321 ----
  //
  HANDLE GetUserToken(DWORD access)
  {
!     HANDLE hTok = NULL;
!     DWORD pid = 0, tid = 0;
  
!     // Try it the easy way first - look for a window owned by the shell on
!     // our current desktop.  If we find one, use that to get the process id.
!     HWND shell = FindWindowEx(NULL, NULL, "Progman", NULL);
!     if (shell != NULL)
!     {
!         tid = GetWindowThreadProcessId(shell, &pid);
!     }
! 
!     // We are possibly running on a private window station and desktop: we must
!     // switch to the default (which we suppose is where we will find the
!     // running shell).
!     else
!     {
!         HWINSTA saveWinSta = GetProcessWindowStation(); 
!         HDESK saveDesk = GetThreadDesktop(GetCurrentThreadId()); 
!         HWINSTA winSta = NULL;
!         HDESK desk = NULL;
!         BOOL changeFlag = FALSE;
!         BOOL dummy = saveWinSta != NULL &&
!                      saveDesk != NULL &&
!                      (winSta = OpenWindowStation("WinSta0", FALSE,
!                                                  MAXIMUM_ALLOWED)) != NULL &&
!                      (changeFlag = SetProcessWindowStation(winSta)) != 0 &&
!                      (desk = OpenDesktop("Default", 0, FALSE,
!                                           MAXIMUM_ALLOWED)) != NULL &&
!                      SetThreadDesktop(desk) != 0;
! 
!         // Now find the window and process on this desktop
!         shell = FindWindowEx(NULL, NULL, "Progman", NULL);
!         if (shell != NULL) 
!         {
!             tid = GetWindowThreadProcessId(shell, &pid);
!         }
! 
!         // Restore our own window station and desktop
!         if (changeFlag)
!         {
!             SetProcessWindowStation(saveWinSta);
!             SetThreadDesktop(saveDesk);
!         }
! 
!         // Close temporary objects
!         if (winSta != NULL)
!             CloseWindowStation(winSta);
!         if (desk != NULL) 
!             CloseDesktop(desk);
!     }
! 
!     //
!     // If we have a process id, use that to get the process handle and 
!     // from there the process' access token.
!     //
!     if (pid != 0)
!     {
!         HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
!         if (hProc != NULL)
!         {
!             OpenProcessToken(hProc, access, &hTok) || (hTok = NULL);
!             CloseHandle(hProc);
!         }
!     }
! 
!     // Return token if we got one
!     return hTok;
! }       
  
  // impersonate logged-on user as client
  BOOL
  ImpersonateClient()
  {
!     DWORD	dwDesiredAccess = TOKEN_ALL_ACCESS;
!     HANDLE	hUserToken = GetUserToken(dwDesiredAccess);
  	
!     if (hUserToken == NULL)
!         return FALSE;
!     if (ImpersonateLoggedOnUser(hUserToken) == 0)
!     {
!         LogEvent(EVENTLOG_ERROR_TYPE, MSG_FLUSH_IMPERSONATE_ERROR,
!                   NULL);
!         return FALSE;
!     }
!     return TRUE;
  }
  	
  /////////////////////////////////////////////////////////////////////
***************
*** 317,383 ****
  DWORD WINAPI 
  afsd_ServiceFlushVolumesThreadProc(LPVOID lpParam)
  {
! 	FLUSHVOLTHREADINFO ThreadInfo;
! 	PFLUSHVOLTHREADINFO pThreadInfo = (PFLUSHVOLTHREADINFO) lpParam; 
! 	HANDLE	arHandles[2] = {0};
! 	DWORD	dwWaitState = 0;
! 
! 	// thread running - get handles
! 	ThreadInfo.hEventPowerEvent = pThreadInfo->hEventPowerEvent;
! 	ThreadInfo.hEventResumeMain = pThreadInfo->hEventResumeMain;
! 	ThreadInfo.hEventTerminate  = pThreadInfo->hEventTerminate;
! 
! 	// setup to wait
! 	arHandles[0] = ThreadInfo.hEventTerminate;
! 	arHandles[1] = ThreadInfo.hEventPowerEvent;
! 
! 	// do stuff ..
! 	while (1)
! 	{
! 		// wait for an event to happen
! 		dwWaitState = WaitForMultipleObjectsEx(2, arHandles, FALSE, INFINITE, FALSE);
! 
! 		switch (dwWaitState)
! 		{
! 		case WAIT_OBJECT_0:
! 			// termination signaled
! 			RevertToSelf();
              CheckAndCloseHandle(ThreadInfo.hEventPowerEvent);
              CheckAndCloseHandle(ThreadInfo.hEventResumeMain);
              CheckAndCloseHandle(ThreadInfo.hEventTerminate);
! 			ExitThread(0);
! 			break;
  
! 		case WAIT_OBJECT_0+1:
! 			// Power event 
! 			// - flush 'em!
! 			if (ImpersonateClient())
! 			{
! 				afsd_ServicePerformFlushVolumes();
! 			}
! 			// acknowledge event
! 			ResetEvent(ThreadInfo.hEventPowerEvent);
! 			break;
! 
! 		case WAIT_ABANDONED_0:
! 		case WAIT_ABANDONED_0+1:
! 		case WAIT_IO_COMPLETION:
! 		case WAIT_TIMEOUT:
! 			// sno*
! 			LogEvent(EVENTLOG_WARNING_TYPE,
! 				 MSG_FLUSH_UNEXPECTED_EVENT, NULL);
! 			break;
! 		
! 		}	// end switch
! 
! 		// signal back to waiting mainline
! 		SetEvent(ThreadInfo.hEventResumeMain);
! 
! 	}	// end while
! 	
! 	// I suppose we never get here
! 	ExitThread(0);
! }
  
  /////////////////////////////////////////////////////////////////////
  //
--- 325,391 ----
  DWORD WINAPI 
  afsd_ServiceFlushVolumesThreadProc(LPVOID lpParam)
  {
!     FLUSHVOLTHREADINFO ThreadInfo;
!     PFLUSHVOLTHREADINFO pThreadInfo = (PFLUSHVOLTHREADINFO) lpParam; 
!     HANDLE	arHandles[2] = {0};
!     DWORD	dwWaitState = 0;
! 
!     // thread running - get handles
!     ThreadInfo.hEventPowerEvent = pThreadInfo->hEventPowerEvent;
!     ThreadInfo.hEventResumeMain = pThreadInfo->hEventResumeMain;
!     ThreadInfo.hEventTerminate  = pThreadInfo->hEventTerminate;
! 
!     // setup to wait
!     arHandles[0] = ThreadInfo.hEventTerminate;
!     arHandles[1] = ThreadInfo.hEventPowerEvent;
! 
!     // do stuff ..
!     while (1)
!     {
!         // wait for an event to happen
!         dwWaitState = WaitForMultipleObjectsEx(2, arHandles, FALSE, INFINITE, FALSE);
! 
!         switch (dwWaitState)
!         {
!         case WAIT_OBJECT_0:
!             // termination signaled
!             RevertToSelf();
              CheckAndCloseHandle(ThreadInfo.hEventPowerEvent);
              CheckAndCloseHandle(ThreadInfo.hEventResumeMain);
              CheckAndCloseHandle(ThreadInfo.hEventTerminate);
!             ExitThread(0);
!             break;
  
!         case WAIT_OBJECT_0+1:
!             // Power event 
!             // - flush 'em!
!             if (ImpersonateClient())
!             {
!                 afsd_ServicePerformFlushVolumes();
!             }
!             // acknowledge event
!             ResetEvent(ThreadInfo.hEventPowerEvent);
!             break;
! 
!         case WAIT_ABANDONED_0:
!         case WAIT_ABANDONED_0+1:
!         case WAIT_IO_COMPLETION:
!         case WAIT_TIMEOUT:
!             // sno*
!             LogEvent(EVENTLOG_WARNING_TYPE,
!                       MSG_FLUSH_UNEXPECTED_EVENT, NULL);
!             break;
! 
!         }	// end switch
! 
!         // signal back to waiting mainline
!         SetEvent(ThreadInfo.hEventResumeMain);
! 
!     }	// end while
! 
!     // I suppose we never get here
!     ExitThread(0);
! }       
  
  /////////////////////////////////////////////////////////////////////
  //
***************
*** 387,397 ****
  VOID	
  CheckAndCloseHandle(HANDLE thisHandle)
  {
! 	if (thisHandle != NULL)
! 	{
! 		CloseHandle(thisHandle);
! 		thisHandle = NULL;
! 	}
  }
  
  //
--- 395,405 ----
  VOID	
  CheckAndCloseHandle(HANDLE thisHandle)
  {
!     if (thisHandle != NULL)
!     {
!         CloseHandle(thisHandle);
!         thisHandle = NULL;
!     }
  }
  
  //
***************
*** 400,461 ****
  BOOL
  PowerNotificationThreadCreate()
  {
! 	BOOL	bSuccess = FALSE;
! 	DWORD	dwThreadId = 0;
      char    eventName[MAX_PATH];
  	
! 	do 
! 	{
! 		// create power event notification event
! 		// bManualReset=TRUE, bInitialState=FALSE
! 		gThreadInfo.hEventPowerEvent = CreateEvent(NULL, TRUE, FALSE, 
                                                     TEXT("afsd_flushvol_EventPowerEvent"));
          if ( GetLastError() == ERROR_ALREADY_EXISTS )
              afsi_log("Event Object Already Exists: %s", eventName);
! 		if (gThreadInfo.hEventPowerEvent == NULL)
! 			break;			
  
! 		// create mainline resume event
! 		// bManualReset=FALSE, bInitialState=FALSE
! 		gThreadInfo.hEventResumeMain = CreateEvent(NULL, FALSE, FALSE, 
                                                     TEXT("afsd_flushvol_EventResumeMain"));
          if ( GetLastError() == ERROR_ALREADY_EXISTS )
              afsi_log("Event Object Already Exists: %s", eventName);
! 		if (gThreadInfo.hEventResumeMain == NULL)
! 			break;			
  
! 		// create thread terminate event
! 		// bManualReset=FALSE, bInitialState=FALSE
! 		gThreadInfo.hEventTerminate = CreateEvent(NULL, FALSE, FALSE, 
                                                    TEXT("afsd_flushvol_EventTerminate"));
          if ( GetLastError() == ERROR_ALREADY_EXISTS )
              afsi_log("Event Object Already Exists: %s", eventName);
! 		if (gThreadInfo.hEventTerminate == NULL)
! 			break;			
  
! 		// good so far - create thread
! 		gThreadHandle = CreateThread(NULL, 0,
! 							afsd_ServiceFlushVolumesThreadProc,
! 							(LPVOID) &gThreadInfo,
! 							0, &dwThreadId);
  		
! 		if (!gThreadHandle)
! 			break;
  
! 		bSuccess = TRUE;
  
! 	} while (0);
  
  
! 	if (!bSuccess)
! 	{
! 		CheckAndCloseHandle(gThreadInfo.hEventPowerEvent);
! 		CheckAndCloseHandle(gThreadInfo.hEventResumeMain);
! 		CheckAndCloseHandle(gThreadInfo.hEventTerminate);
! 		CheckAndCloseHandle(gThreadHandle);
! 	}
  		
! 	return bSuccess;
  }
  
  //
--- 408,469 ----
  BOOL
  PowerNotificationThreadCreate()
  {
!     BOOL	bSuccess = FALSE;
!     DWORD	dwThreadId = 0;
      char    eventName[MAX_PATH];
  	
!     do 
!     {
!         // create power event notification event
!         // bManualReset=TRUE, bInitialState=FALSE
!         gThreadInfo.hEventPowerEvent = CreateEvent(NULL, TRUE, FALSE, 
                                                     TEXT("afsd_flushvol_EventPowerEvent"));
          if ( GetLastError() == ERROR_ALREADY_EXISTS )
              afsi_log("Event Object Already Exists: %s", eventName);
!         if (gThreadInfo.hEventPowerEvent == NULL)
!             break;			
  
!         // create mainline resume event
!         // bManualReset=FALSE, bInitialState=FALSE
!         gThreadInfo.hEventResumeMain = CreateEvent(NULL, FALSE, FALSE, 
                                                     TEXT("afsd_flushvol_EventResumeMain"));
          if ( GetLastError() == ERROR_ALREADY_EXISTS )
              afsi_log("Event Object Already Exists: %s", eventName);
!         if (gThreadInfo.hEventResumeMain == NULL)
!             break;			
  
!         // create thread terminate event
!         // bManualReset=FALSE, bInitialState=FALSE
!         gThreadInfo.hEventTerminate = CreateEvent(NULL, FALSE, FALSE, 
                                                    TEXT("afsd_flushvol_EventTerminate"));
          if ( GetLastError() == ERROR_ALREADY_EXISTS )
              afsi_log("Event Object Already Exists: %s", eventName);
!         if (gThreadInfo.hEventTerminate == NULL)
!             break;			
  
!         // good so far - create thread
!         gThreadHandle = CreateThread(NULL, 0,
!                                      afsd_ServiceFlushVolumesThreadProc,
!                                      (LPVOID) &gThreadInfo,
!                                      0, &dwThreadId);
  		
!         if (!gThreadHandle)
!             break;
  
!         bSuccess = TRUE;
  
!     } while (0);
  
  
!     if (!bSuccess)
!     {
!         CheckAndCloseHandle(gThreadInfo.hEventPowerEvent);
!         CheckAndCloseHandle(gThreadInfo.hEventResumeMain);
!         CheckAndCloseHandle(gThreadInfo.hEventTerminate);
!         CheckAndCloseHandle(gThreadHandle);
!     }
  		
!     return bSuccess;
  }
  
  //
***************
*** 464,484 ****
  BOOL
  PowerNotificationThreadNotify()
  {
! 	DWORD		dwRet = 0;
! 	BOOL		bRet  = FALSE;
  
! 	// Notify thread of power event, and wait for the HardDead timeout period
! 	dwRet = SignalObjectAndWait(
! 				gThreadInfo.hEventPowerEvent,	// object to signal
! 				gThreadInfo.hEventResumeMain,	// object to watch
! 				HardDeadtimeout*1000,			// timeout (ms)
! 				FALSE							// alertable
! 				);
  
! 	if (dwRet == WAIT_OBJECT_0)
! 		bRet = TRUE;
  
! 	return bRet;
  }
  
  //
--- 472,492 ----
  BOOL
  PowerNotificationThreadNotify()
  {
!     DWORD		dwRet = 0;
!     BOOL		bRet  = FALSE;
  
!     // Notify thread of power event, and wait for the HardDead timeout period
!     dwRet = SignalObjectAndWait(
!                 gThreadInfo.hEventPowerEvent,	// object to signal
!                 gThreadInfo.hEventResumeMain,	// object to watch
! 		HardDeadtimeout*1000,		// timeout (ms)
! 		FALSE				// alertable
! 		);
  
!     if (dwRet == WAIT_OBJECT_0)
!         bRet = TRUE;
  
!     return bRet;
  }
  
  //
***************
*** 487,498 ****
  VOID
  PowerNotificationThreadExit()
  {
! 	// ExitThread
! 	if (gThreadHandle)
! 	{
! 		SetEvent(gThreadInfo.hEventTerminate);
          WaitForSingleObject(gThreadHandle, INFINITE);
! 		CloseHandle(gThreadHandle);
! 	}
  }
  
--- 495,506 ----
  VOID
  PowerNotificationThreadExit()
  {
!     // ExitThread
!     if (gThreadHandle)
!     {
!         SetEvent(gThreadInfo.hEventTerminate);
          WaitForSingleObject(gThreadHandle, INFINITE);
!         CloseHandle(gThreadHandle);
!     }
  }
  
Index: openafs/src/WINNT/afsd/afsd_init.c
diff -c openafs/src/WINNT/afsd/afsd_init.c:1.40.2.2 openafs/src/WINNT/afsd/afsd_init.c:1.40.2.5
*** openafs/src/WINNT/afsd/afsd_init.c:1.40.2.2	Tue Aug 17 00:28:38 2004
--- openafs/src/WINNT/afsd/afsd_init.c	Mon Oct 18 00:09:25 2004
***************
*** 81,87 ****
  cm_initparams_v1 cm_initParams;
  
  char *cm_sysName = 0;
! int   cm_sysNameCount = 0;
  char *cm_sysNameList[MAXNUMSYSNAMES];
  
  DWORD TraceOption = 0;
--- 81,87 ----
  cm_initparams_v1 cm_initParams;
  
  char *cm_sysName = 0;
! unsigned int   cm_sysNameCount = 0;
  char *cm_sysNameList[MAXNUMSYSNAMES];
  
  DWORD TraceOption = 0;
***************
*** 358,429 ****
  
  int afsd_InitCM(char **reasonP)
  {
! 	osi_uid_t debugID;
! 	long cacheBlocks;
! 	long cacheSize;
! 	long logChunkSize;
! 	long stats;
! 	long traceBufSize;
      long maxcpus;
! 	long ltt, ltto;
      long rx_mtu, rx_nojumbo;
      long virtualCache;
! 	char rootCellName[256];
! 	struct rx_service *serverp;
! 	static struct rx_securityClass *nullServerSecurityClassp;
! 	struct hostent *thp;
! 	char *msgBuf;
! 	char buf[200];
! 	HKEY parmKey;
! 	DWORD dummyLen;
      DWORD regType;
! 	long code;
! 	/*int freelanceEnabled;*/
! 	WSADATA WSAjunk;
      lana_number_t lanaNum;
      int i;
  
! 	WSAStartup(0x0101, &WSAjunk);
  
      afsd_initUpperCaseTable();
  
! 	/* setup osidebug server at RPC slot 1000 */
! 	osi_LongToUID(1000, &debugID);
! 	code = osi_InitDebug(&debugID);
! 	afsi_log("osi_InitDebug code %d", code);
  
      //	osi_LockTypeSetDefault("stat");	/* comment this out for speed *
! 	if (code != 0) {
! 		*reasonP = "unknown error";
! 		return -1;
! 	}
  
! 	/* who are we ? */
! 	gethostname(cm_HostName, sizeof(cm_HostName));
! 	afsi_log("gethostname %s", cm_HostName);
! 	thp = gethostbyname(cm_HostName);
! 	memcpy(&cm_HostAddr, thp->h_addr_list[0], 4);
! 
! 	/* seed random number generator */
! 	srand(ntohl(cm_HostAddr));
! 
! 	/* Look up configuration parameters in Registry */
! 	code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSConfigKeyName,
! 				0, KEY_QUERY_VALUE, &parmKey);
! 	if (code != ERROR_SUCCESS) {
! 		FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
! 				| FORMAT_MESSAGE_ALLOCATE_BUFFER,
! 			      NULL, code, 0, (LPTSTR)&msgBuf, 0, NULL);
! 		StringCbPrintfA(buf, sizeof(buf),
! 			"Failure in configuration while opening Registry: %s",
! 			msgBuf);
! 		osi_panic(buf, __FILE__, __LINE__);
! 	}
  
      dummyLen = sizeof(maxcpus);
! 	code = RegQueryValueEx(parmKey, "MaxCPUs", NULL, NULL,
! 				(BYTE *) &maxcpus, &dummyLen);
! 	if (code == ERROR_SUCCESS) {
          HANDLE hProcess;
          DWORD_PTR processAffinityMask, systemAffinityMask;
  
--- 358,429 ----
  
  int afsd_InitCM(char **reasonP)
  {
!     osi_uid_t debugID;
!     long cacheBlocks;
!     long cacheSize;
!     long logChunkSize;
!     long stats;
!     long traceBufSize;
      long maxcpus;
!     long ltt, ltto;
      long rx_mtu, rx_nojumbo;
      long virtualCache;
!     char rootCellName[256];
!     struct rx_service *serverp;
!     static struct rx_securityClass *nullServerSecurityClassp;
!     struct hostent *thp;
!     char *msgBuf;
!     char buf[1024];
!     HKEY parmKey;
!     DWORD dummyLen;
      DWORD regType;
!     long code;
!     /*int freelanceEnabled;*/
!     WSADATA WSAjunk;
      lana_number_t lanaNum;
      int i;
  
!     WSAStartup(0x0101, &WSAjunk);
  
      afsd_initUpperCaseTable();
  
!     /* setup osidebug server at RPC slot 1000 */
!     osi_LongToUID(1000, &debugID);
!     code = osi_InitDebug(&debugID);
!     afsi_log("osi_InitDebug code %d", code);
  
      //	osi_LockTypeSetDefault("stat");	/* comment this out for speed *
!     if (code != 0) {
!         *reasonP = "unknown error";
!         return -1;
!     }
  
!     /* who are we ? */
!     gethostname(cm_HostName, sizeof(cm_HostName));
!     afsi_log("gethostname %s", cm_HostName);
!     thp = gethostbyname(cm_HostName);
!     memcpy(&cm_HostAddr, thp->h_addr_list[0], 4);
! 
!     /* seed random number generator */
!     srand(ntohl(cm_HostAddr));
! 
!     /* Look up configuration parameters in Registry */
!     code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSConfigKeyName,
!                          0, KEY_QUERY_VALUE, &parmKey);
!     if (code != ERROR_SUCCESS) {
!         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
!                        | FORMAT_MESSAGE_ALLOCATE_BUFFER,
!                        NULL, code, 0, (LPTSTR)&msgBuf, 0, NULL);
!         StringCbPrintfA(buf, sizeof(buf),
!                          "Failure in configuration while opening Registry: %s",
!                          msgBuf);
!         osi_panic(buf, __FILE__, __LINE__);
!     }
  
      dummyLen = sizeof(maxcpus);
!     code = RegQueryValueEx(parmKey, "MaxCPUs", NULL, NULL,
!                             (BYTE *) &maxcpus, &dummyLen);
!     if (code == ERROR_SUCCESS) {
          HANDLE hProcess;
          DWORD_PTR processAffinityMask, systemAffinityMask;
  
***************
*** 456,596 ****
          }
      }
  
! 	dummyLen = sizeof(TraceOption);
! 	code = RegQueryValueEx(parmKey, "TraceOption", NULL, NULL,
! 				(BYTE *) &TraceOption, &dummyLen);
      afsi_log("Event Log Tracing = %lX", TraceOption);
  
! 	dummyLen = sizeof(traceBufSize);
! 	code = RegQueryValueEx(parmKey, "TraceBufferSize", NULL, NULL,
! 				(BYTE *) &traceBufSize, &dummyLen);
! 	if (code == ERROR_SUCCESS)
! 		afsi_log("Trace Buffer size %d", traceBufSize);
! 	else {
! 		traceBufSize = CM_CONFIGDEFAULT_TRACEBUFSIZE;
! 		afsi_log("Default trace buffer size %d", traceBufSize);
! 	}
  
! 	/* setup and enable debug log */
! 	afsd_logp = osi_LogCreate("afsd", traceBufSize);
! 	afsi_log("osi_LogCreate log addr %x", (int)afsd_logp);
      osi_LogEnable(afsd_logp);
! 	logReady = 1;
  
      osi_Log0(afsd_logp, "Log init");
  
! 	dummyLen = sizeof(cacheSize);
! 	code = RegQueryValueEx(parmKey, "CacheSize", NULL, NULL,
! 				(BYTE *) &cacheSize, &dummyLen);
! 	if (code == ERROR_SUCCESS)
! 		afsi_log("Cache size %d", cacheSize);
! 	else {
! 		cacheSize = CM_CONFIGDEFAULT_CACHESIZE;
! 		afsi_log("Default cache size %d", cacheSize);
! 	}
  
! 	dummyLen = sizeof(logChunkSize);
! 	code = RegQueryValueEx(parmKey, "ChunkSize", NULL, NULL,
! 				(BYTE *) &logChunkSize, &dummyLen);
! 	if (code == ERROR_SUCCESS) {
! 		if (logChunkSize < 12 || logChunkSize > 30) {
! 			afsi_log("Invalid chunk size %d, using default",
! 				 logChunkSize);
! 			logChunkSize = CM_CONFIGDEFAULT_CHUNKSIZE;
! 		}
! 		afsi_log("Chunk size %d", logChunkSize);
! 	} else {
! 		logChunkSize = CM_CONFIGDEFAULT_CHUNKSIZE;
! 		afsi_log("Default chunk size %d", logChunkSize);
! 	}
! 	cm_logChunkSize = logChunkSize;
! 	cm_chunkSize = 1 << logChunkSize;
  
! 	dummyLen = sizeof(numBkgD);
! 	code = RegQueryValueEx(parmKey, "Daemons", NULL, NULL,
! 				(BYTE *) &numBkgD, &dummyLen);
! 	if (code == ERROR_SUCCESS)
! 		afsi_log("%d background daemons", numBkgD);
! 	else {
! 		numBkgD = CM_CONFIGDEFAULT_DAEMONS;
! 		afsi_log("Defaulting to %d background daemons", numBkgD);
! 	}
  
! 	dummyLen = sizeof(numSvThreads);
! 	code = RegQueryValueEx(parmKey, "ServerThreads", NULL, NULL,
! 				(BYTE *) &numSvThreads, &dummyLen);
! 	if (code == ERROR_SUCCESS)
! 		afsi_log("%d server threads", numSvThreads);
! 	else {
! 		numSvThreads = CM_CONFIGDEFAULT_SVTHREADS;
! 		afsi_log("Defaulting to %d server threads", numSvThreads);
! 	}
  
! 	dummyLen = sizeof(stats);
! 	code = RegQueryValueEx(parmKey, "Stats", NULL, NULL,
! 				(BYTE *) &stats, &dummyLen);
! 	if (code == ERROR_SUCCESS)
! 		afsi_log("Status cache size %d", stats);
! 	else {
! 		stats = CM_CONFIGDEFAULT_STATS;
! 		afsi_log("Default status cache size %d", stats);
! 	}
  
! 	dummyLen = sizeof(ltt);
! 	code = RegQueryValueEx(parmKey, "LogoffTokenTransfer", NULL, NULL,
! 				(BYTE *) &ltt, &dummyLen);
! 	if (code == ERROR_SUCCESS)
! 		afsi_log("Logoff token transfer %s",  (ltt ? "on" : "off"));
! 	else {
! 		ltt = 1;
! 		afsi_log("Logoff token transfer on by default");
! 	}
      smb_LogoffTokenTransfer = ltt;
      afsi_log("Logoff token transfer is currently ignored");
  
! 	if (ltt) {
! 		dummyLen = sizeof(ltto);
! 		code = RegQueryValueEx(parmKey, "LogoffTokenTransferTimeout",
! 					NULL, NULL, (BYTE *) &ltto, &dummyLen);
! 		if (code == ERROR_SUCCESS)
              afsi_log("Logoff token tranfer timeout %d seconds", ltto);
! 		else {
! 			ltto = 10;
! 			afsi_log("Default logoff token transfer timeout 10 seconds");
! 		}
! 	} else {
          ltto = 0;
!     }
      smb_LogoffTransferTimeout = ltto;
      afsi_log("Default logoff token is currently ignored");
  
! 	dummyLen = sizeof(cm_rootVolumeName);
! 	code = RegQueryValueEx(parmKey, "RootVolume", NULL, NULL,
! 				cm_rootVolumeName, &dummyLen);
! 	if (code == ERROR_SUCCESS)
! 		afsi_log("Root volume %s", cm_rootVolumeName);
! 	else {
! 		StringCbCopyA(cm_rootVolumeName, sizeof(cm_rootVolumeName), "root.afs");
! 		afsi_log("Default root volume name root.afs");
! 	}
  
! 	cm_mountRootLen = sizeof(cm_mountRoot);
! 	code = RegQueryValueEx(parmKey, "MountRoot", NULL, NULL,
! 				cm_mountRoot, &cm_mountRootLen);
! 	if (code == ERROR_SUCCESS) {
! 		afsi_log("Mount root %s", cm_mountRoot);
! 		cm_mountRootLen = strlen(cm_mountRoot);
! 	} else {
! 		StringCbCopyA(cm_mountRoot, sizeof(cm_mountRoot), "/afs");
! 		cm_mountRootLen = 4;
! 		/* Don't log */
! 	}
  
! 	dummyLen = sizeof(buf);
! 	code = RegQueryValueEx(parmKey, "CachePath", NULL, &regType,
! 				buf, &dummyLen);
      if (code == ERROR_SUCCESS && buf[0]) {
!         if(regType == REG_EXPAND_SZ) {
              dummyLen = ExpandEnvironmentStrings(buf, cm_CachePath, sizeof(cm_CachePath));
              if(dummyLen > sizeof(cm_CachePath)) {
                  afsi_log("Cache path [%s] longer than %d after expanding env strings", buf, sizeof(cm_CachePath));
--- 456,596 ----
          }
      }
  
!     dummyLen = sizeof(TraceOption);
!     code = RegQueryValueEx(parmKey, "TraceOption", NULL, NULL,
!                             (BYTE *) &TraceOption, &dummyLen);
      afsi_log("Event Log Tracing = %lX", TraceOption);
  
!     dummyLen = sizeof(traceBufSize);
!     code = RegQueryValueEx(parmKey, "TraceBufferSize", NULL, NULL,
!                             (BYTE *) &traceBufSize, &dummyLen);
!     if (code == ERROR_SUCCESS)
!         afsi_log("Trace Buffer size %d", traceBufSize);
!     else {
!         traceBufSize = CM_CONFIGDEFAULT_TRACEBUFSIZE;
!         afsi_log("Default trace buffer size %d", traceBufSize);
!     }
  
!     /* setup and enable debug log */
!     afsd_logp = osi_LogCreate("afsd", traceBufSize);
!     afsi_log("osi_LogCreate log addr %x", (int)afsd_logp);
      osi_LogEnable(afsd_logp);
!     logReady = 1;
  
      osi_Log0(afsd_logp, "Log init");
  
!     dummyLen = sizeof(cacheSize);
!     code = RegQueryValueEx(parmKey, "CacheSize", NULL, NULL,
!                             (BYTE *) &cacheSize, &dummyLen);
!     if (code == ERROR_SUCCESS)
!         afsi_log("Cache size %d", cacheSize);
!     else {
!         cacheSize = CM_CONFIGDEFAULT_CACHESIZE;
!         afsi_log("Default cache size %d", cacheSize);
!     }
  
!     dummyLen = sizeof(logChunkSize);
!     code = RegQueryValueEx(parmKey, "ChunkSize", NULL, NULL,
!                             (BYTE *) &logChunkSize, &dummyLen);
!     if (code == ERROR_SUCCESS) {
!         if (logChunkSize < 12 || logChunkSize > 30) {
!             afsi_log("Invalid chunk size %d, using default",
!                       logChunkSize);
!             logChunkSize = CM_CONFIGDEFAULT_CHUNKSIZE;
!         }
!         afsi_log("Chunk size %d", logChunkSize);
!     } else {
!         logChunkSize = CM_CONFIGDEFAULT_CHUNKSIZE;
!         afsi_log("Default chunk size %d", logChunkSize);
!     }
!     cm_logChunkSize = logChunkSize;
!     cm_chunkSize = 1 << logChunkSize;
  
!     dummyLen = sizeof(numBkgD);
!     code = RegQueryValueEx(parmKey, "Daemons", NULL, NULL,
!                             (BYTE *) &numBkgD, &dummyLen);
!     if (code == ERROR_SUCCESS)
!         afsi_log("%d background daemons", numBkgD);
!     else {
!         numBkgD = CM_CONFIGDEFAULT_DAEMONS;
!         afsi_log("Defaulting to %d background daemons", numBkgD);
!     }
  
!     dummyLen = sizeof(numSvThreads);
!     code = RegQueryValueEx(parmKey, "ServerThreads", NULL, NULL,
!                             (BYTE *) &numSvThreads, &dummyLen);
!     if (code == ERROR_SUCCESS)
!         afsi_log("%d server threads", numSvThreads);
!     else {
!         numSvThreads = CM_CONFIGDEFAULT_SVTHREADS;
!         afsi_log("Defaulting to %d server threads", numSvThreads);
!     }
  
!     dummyLen = sizeof(stats);
!     code = RegQueryValueEx(parmKey, "Stats", NULL, NULL,
!                             (BYTE *) &stats, &dummyLen);
!     if (code == ERROR_SUCCESS)
!         afsi_log("Status cache size %d", stats);
!     else {
!         stats = CM_CONFIGDEFAULT_STATS;
!         afsi_log("Default status cache size %d", stats);
!     }
  
!     dummyLen = sizeof(ltt);
!     code = RegQueryValueEx(parmKey, "LogoffTokenTransfer", NULL, NULL,
!                             (BYTE *) &ltt, &dummyLen);
!     if (code == ERROR_SUCCESS)
!         afsi_log("Logoff token transfer %s",  (ltt ? "on" : "off"));
!     else {
!         ltt = 1;
!         afsi_log("Logoff token transfer on by default");
!     }
      smb_LogoffTokenTransfer = ltt;
      afsi_log("Logoff token transfer is currently ignored");
  
!     if (ltt) {
!         dummyLen = sizeof(ltto);
!         code = RegQueryValueEx(parmKey, "LogoffTokenTransferTimeout",
!                                 NULL, NULL, (BYTE *) &ltto, &dummyLen);
!         if (code == ERROR_SUCCESS)
              afsi_log("Logoff token tranfer timeout %d seconds", ltto);
!         else {
!             ltto = 10;
!             afsi_log("Default logoff token transfer timeout 10 seconds");
!         }
!     } else {
          ltto = 0;
!     }   
      smb_LogoffTransferTimeout = ltto;
      afsi_log("Default logoff token is currently ignored");
  
!     dummyLen = sizeof(cm_rootVolumeName);
!     code = RegQueryValueEx(parmKey, "RootVolume", NULL, NULL,
!                             cm_rootVolumeName, &dummyLen);
!     if (code == ERROR_SUCCESS)
!         afsi_log("Root volume %s", cm_rootVolumeName);
!     else {
!         StringCbCopyA(cm_rootVolumeName, sizeof(cm_rootVolumeName), "root.afs");
!         afsi_log("Default root volume name root.afs");
!     }
  
!     cm_mountRootLen = sizeof(cm_mountRoot);
!     code = RegQueryValueEx(parmKey, "MountRoot", NULL, NULL,
!                             cm_mountRoot, &cm_mountRootLen);
!     if (code == ERROR_SUCCESS) {
!         afsi_log("Mount root %s", cm_mountRoot);
!         cm_mountRootLen = strlen(cm_mountRoot);
!     } else {
!         StringCbCopyA(cm_mountRoot, sizeof(cm_mountRoot), "/afs");
!         cm_mountRootLen = 4;
!         /* Don't log */
!     }
  
!     dummyLen = sizeof(buf);
!     code = RegQueryValueEx(parmKey, "CachePath", NULL, &regType,
!                             buf, &dummyLen);
      if (code == ERROR_SUCCESS && buf[0]) {
!         if (regType == REG_EXPAND_SZ) {
              dummyLen = ExpandEnvironmentStrings(buf, cm_CachePath, sizeof(cm_CachePath));
              if(dummyLen > sizeof(cm_CachePath)) {
                  afsi_log("Cache path [%s] longer than %d after expanding env strings", buf, sizeof(cm_CachePath));
***************
*** 599,615 ****
          } else {
              StringCbCopyA(cm_CachePath, sizeof(cm_CachePath), buf);
          }
! 		afsi_log("Cache path %s", cm_CachePath);
      } else {
! 		GetWindowsDirectory(cm_CachePath, sizeof(cm_CachePath));
! 		cm_CachePath[2] = 0;	/* get drive letter only */
! 		StringCbCatA(cm_CachePath, sizeof(cm_CachePath), "\\AFSCache");
! 		afsi_log("Default cache path %s", cm_CachePath);
! 	}
  
      dummyLen = sizeof(virtualCache);
      code = RegQueryValueEx(parmKey, "NonPersistentCaching", NULL, NULL,
!         &virtualCache, &dummyLen);
      if (code == ERROR_SUCCESS && virtualCache) {
          buf_cacheType = CM_BUF_CACHETYPE_VIRTUAL;
      } else {
--- 599,615 ----
          } else {
              StringCbCopyA(cm_CachePath, sizeof(cm_CachePath), buf);
          }
!         afsi_log("Cache path %s", cm_CachePath);
      } else {
!         GetWindowsDirectory(cm_CachePath, sizeof(cm_CachePath));
!         cm_CachePath[2] = 0;	/* get drive letter only */
!         StringCbCatA(cm_CachePath, sizeof(cm_CachePath), "\\AFSCache");
!         afsi_log("Default cache path %s", cm_CachePath);
!     }
  
      dummyLen = sizeof(virtualCache);
      code = RegQueryValueEx(parmKey, "NonPersistentCaching", NULL, NULL,
!                             &virtualCache, &dummyLen);
      if (code == ERROR_SUCCESS && virtualCache) {
          buf_cacheType = CM_BUF_CACHETYPE_VIRTUAL;
      } else {
***************
*** 617,643 ****
      }
      afsi_log("Cache type is %s", ((buf_cacheType == CM_BUF_CACHETYPE_FILE)?"FILE":"VIRTUAL"));
  
! 	dummyLen = sizeof(traceOnPanic);
! 	code = RegQueryValueEx(parmKey, "TrapOnPanic", NULL, NULL,
! 				(BYTE *) &traceOnPanic, &dummyLen);
! 	if (code == ERROR_SUCCESS)
! 		afsi_log("Set to %s on panic",
! 			 traceOnPanic ? "trap" : "not trap");
! 	else {
! 		traceOnPanic = 0;
! 		/* Don't log */
! 	}
! 
! 	dummyLen = sizeof(reportSessionStartups);
! 	code = RegQueryValueEx(parmKey, "ReportSessionStartups", NULL, NULL,
! 				(BYTE *) &reportSessionStartups, &dummyLen);
! 	if (code == ERROR_SUCCESS)
! 		afsi_log("Session startups %s be recorded in the Event Log",
! 			 reportSessionStartups ? "will" : "will not");
! 	else {
! 		reportSessionStartups = 0;
! 		/* Don't log */
! 	}
  
      for ( i=0; i < MAXNUMSYSNAMES; i++ ) {
          cm_sysNameList[i] = osi_Alloc(MAXSYSNAME);
--- 617,643 ----
      }
      afsi_log("Cache type is %s", ((buf_cacheType == CM_BUF_CACHETYPE_FILE)?"FILE":"VIRTUAL"));
  
!     dummyLen = sizeof(traceOnPanic);
!     code = RegQueryValueEx(parmKey, "TrapOnPanic", NULL, NULL,
!                             (BYTE *) &traceOnPanic, &dummyLen);
!     if (code == ERROR_SUCCESS)
!         afsi_log("Set to %s on panic",
!                   traceOnPanic ? "trap" : "not trap");
!     else {  
!         traceOnPanic = 0;
!         /* Don't log */
!     }
! 
!     dummyLen = sizeof(reportSessionStartups);
!     code = RegQueryValueEx(parmKey, "ReportSessionStartups", NULL, NULL,
!                             (BYTE *) &reportSessionStartups, &dummyLen);
!     if (code == ERROR_SUCCESS)
!         afsi_log("Session startups %s be recorded in the Event Log",
!                   reportSessionStartups ? "will" : "will not");
!     else {
!         reportSessionStartups = 0;
!         /* Don't log */
!     }
  
      for ( i=0; i < MAXNUMSYSNAMES; i++ ) {
          cm_sysNameList[i] = osi_Alloc(MAXSYSNAME);
***************
*** 645,697 ****
      }
      cm_sysName = cm_sysNameList[0];
  
! 	dummyLen = MAXSYSNAME;
!     code = RegQueryValueEx(parmKey, "SysName", NULL, NULL, cm_sysName, &dummyLen);
! 	if (code == ERROR_SUCCESS)
! 		afsi_log("Sys name %s", cm_sysName);
! 	else {
! 		StringCbCopyA(cm_sysName, MAXSYSNAME, "i386_nt40");
! 		afsi_log("Default sys name %s", cm_sysName);
! 	}
!     cm_sysNameCount = 1;
  
! 	dummyLen = sizeof(cryptall);
! 	code = RegQueryValueEx(parmKey, "SecurityLevel", NULL, NULL,
! 				(BYTE *) &cryptall, &dummyLen);
! 	if (code == ERROR_SUCCESS)
! 		afsi_log("SecurityLevel is %s", cryptall?"crypt":"clear");
! 	else {
! 		cryptall = rxkad_clear;
! 		afsi_log("Default SecurityLevel is clear");
! 	}
  
  #ifdef AFS_AFSDB_ENV
! 	dummyLen = sizeof(cm_dnsEnabled);
! 	code = RegQueryValueEx(parmKey, "UseDNS", NULL, NULL,
! 				(BYTE *) &cm_dnsEnabled, &dummyLen);
! 	if (code == ERROR_SUCCESS) {
! 		afsi_log("DNS %s be used to find AFS cell servers",
! 			 cm_dnsEnabled ? "will" : "will not");
! 	}
! 	else {
! 	  cm_dnsEnabled = 1;   /* default on */
! 	  afsi_log("Default to use DNS to find AFS cell servers");
! 	}
  #else /* AFS_AFSDB_ENV */
! 	afsi_log("AFS not built with DNS support to find AFS cell servers");
  #endif /* AFS_AFSDB_ENV */
  
  #ifdef AFS_FREELANCE_CLIENT
! 	dummyLen = sizeof(cm_freelanceEnabled);
! 	code = RegQueryValueEx(parmKey, "FreelanceClient", NULL, NULL,
! 				(BYTE *) &cm_freelanceEnabled, &dummyLen);
! 	if (code == ERROR_SUCCESS) {
! 		afsi_log("Freelance client feature %s activated",
! 			 cm_freelanceEnabled ? "is" : "is not");
! 	}
! 	else {
! 	  cm_freelanceEnabled = 0;  /* default off */
! 	}
  #endif /* AFS_FREELANCE_CLIENT */
  
  #ifdef COMMENT
--- 645,719 ----
      }
      cm_sysName = cm_sysNameList[0];
  
!     dummyLen = sizeof(buf);
!     code = RegQueryValueEx(parmKey, "SysName", NULL, NULL, buf, &dummyLen);
!     if (code == ERROR_SUCCESS && buf[0]) {
!         char * p, *q; 
!         afsi_log("Sys name %s", buf);
  
!         for (p = q = buf; p < cm_sysName + dummyLen; p++)
!         {
!             if (*p == '\0' || isspace(*p)) {
!                 memcpy(cm_sysNameList[cm_sysNameCount],q,p-q);
!                 cm_sysNameList[cm_sysNameCount][p-q] = '\0';
!                 cm_sysNameCount++;
! 
!                 do {
!                     if (*p == '\0')
!                         goto done_sysname;
!                         
!                     p++;
!                 } while (*p == '\0' || isspace(*p));
!                 q = p;
!                 p--;
!             }
!         }
!       done_sysname:
!         StringCbCopyA(cm_sysName, MAXSYSNAME, cm_sysNameList[0]);
!     } else {
!         cm_sysNameCount = 1;
!         StringCbCopyA(cm_sysName, MAXSYSNAME, "i386_nt40");
!         StringCbCopyA(cm_sysNameList[0], MAXSYSNAME, "i386_nt40");
!         afsi_log("Default sys name %s", cm_sysName);
!     }
! 
!     dummyLen = sizeof(cryptall);
!     code = RegQueryValueEx(parmKey, "SecurityLevel", NULL, NULL,
!                             (BYTE *) &cryptall, &dummyLen);
!     if (code == ERROR_SUCCESS) {
!         afsi_log("SecurityLevel is %s", cryptall?"crypt":"clear");
!     } else {
!         cryptall = 0;
!         afsi_log("Default SecurityLevel is clear");
!     }
  
  #ifdef AFS_AFSDB_ENV
!     dummyLen = sizeof(cm_dnsEnabled);
!     code = RegQueryValueEx(parmKey, "UseDNS", NULL, NULL,
!                             (BYTE *) &cm_dnsEnabled, &dummyLen);
!     if (code == ERROR_SUCCESS) {
!         afsi_log("DNS %s be used to find AFS cell servers",
!                   cm_dnsEnabled ? "will" : "will not");
!     }       
!     else {
!         cm_dnsEnabled = 1;   /* default on */
!         afsi_log("Default to use DNS to find AFS cell servers");
!     }
  #else /* AFS_AFSDB_ENV */
!     afsi_log("AFS not built with DNS support to find AFS cell servers");
  #endif /* AFS_AFSDB_ENV */
  
  #ifdef AFS_FREELANCE_CLIENT
!     dummyLen = sizeof(cm_freelanceEnabled);
!     code = RegQueryValueEx(parmKey, "FreelanceClient", NULL, NULL,
!                             (BYTE *) &cm_freelanceEnabled, &dummyLen);
!     if (code == ERROR_SUCCESS) {
!         afsi_log("Freelance client feature %s activated",
!                   cm_freelanceEnabled ? "is" : "is not");
!     }       
!     else {
!         cm_freelanceEnabled = 0;  /* default off */
!     }
  #endif /* AFS_FREELANCE_CLIENT */
  
  #ifdef COMMENT
***************
*** 738,752 ****
      }
      afsi_log("Maximum number of VCs per server is %d", smb_maxVCPerServer);
  
! 	dummyLen = sizeof(smb_authType);
! 	code = RegQueryValueEx(parmKey, "SMBAuthType", NULL, NULL,
! 		(BYTE *) &smb_authType, &dummyLen);
! 
! 	if (code != ERROR_SUCCESS || 
!         (smb_authType != SMB_AUTH_EXTENDED && smb_authType != SMB_AUTH_NTLM && smb_authType != SMB_AUTH_NONE)) {
! 		smb_authType = SMB_AUTH_EXTENDED; /* default is to use extended authentication */
! 	}
! 	afsi_log("SMB authentication type is %s", ((smb_authType == SMB_AUTH_NONE)?"NONE":((smb_authType == SMB_AUTH_EXTENDED)?"EXTENDED":"NTLM")));
  
      dummyLen = sizeof(rx_nojumbo);
      code = RegQueryValueEx(parmKey, "RxNoJumbo", NULL, NULL,
--- 760,774 ----
      }
      afsi_log("Maximum number of VCs per server is %d", smb_maxVCPerServer);
  
!     dummyLen = sizeof(smb_authType);
!     code = RegQueryValueEx(parmKey, "SMBAuthType", NULL, NULL,
!                             (BYTE *) &smb_authType, &dummyLen);
! 
!     if (code != ERROR_SUCCESS || 
!          (smb_authType != SMB_AUTH_EXTENDED && smb_authType != SMB_AUTH_NTLM && smb_authType != SMB_AUTH_NONE)) {
!         smb_authType = SMB_AUTH_EXTENDED; /* default is to use extended authentication */
!     }
!     afsi_log("SMB authentication type is %s", ((smb_authType == SMB_AUTH_NONE)?"NONE":((smb_authType == SMB_AUTH_EXTENDED)?"EXTENDED":"NTLM")));
  
      dummyLen = sizeof(rx_nojumbo);
      code = RegQueryValueEx(parmKey, "RxNoJumbo", NULL, NULL,
***************
*** 754,760 ****
      if (code != ERROR_SUCCESS) {
          rx_nojumbo = 0;
      }
!     if(rx_nojumbo)
          afsi_log("RX Jumbograms are disabled");
  
      dummyLen = sizeof(rx_mtu);
--- 776,782 ----
      if (code != ERROR_SUCCESS) {
          rx_nojumbo = 0;
      }
!     if (rx_nojumbo)
          afsi_log("RX Jumbograms are disabled");
  
      dummyLen = sizeof(rx_mtu);
***************
*** 763,769 ****
      if (code != ERROR_SUCCESS || !rx_mtu) {
          rx_mtu = -1;
      }
!     if(rx_mtu != -1)
          afsi_log("RX maximum MTU is %d", rx_mtu);
  
      dummyLen = sizeof(ConnDeadtimeout);
--- 785,791 ----
      if (code != ERROR_SUCCESS || !rx_mtu) {
          rx_mtu = -1;
      }
!     if (rx_mtu != -1)
          afsi_log("RX maximum MTU is %d", rx_mtu);
  
      dummyLen = sizeof(ConnDeadtimeout);
***************
*** 776,793 ****
                             (BYTE *) &HardDeadtimeout, &dummyLen);
      afsi_log("HardDeadTimeout is %d", HardDeadtimeout);
  
! 	RegCloseKey (parmKey);
  
      /* Call lanahelper to get Netbios name, lan adapter number and gateway flag */
      if(SUCCEEDED(code = lana_GetUncServerNameEx(cm_NetbiosName, &lanaNum, &isGateway, LANA_NETBIOS_NAME_FULL))) {
          LANadapter = (lanaNum == LANA_INVALID)? -1: lanaNum;
  
!         if(LANadapter != -1)
              afsi_log("LAN adapter number %d", LANadapter);
          else
              afsi_log("LAN adapter number not determined");
  
!         if(isGateway)
              afsi_log("Set for gateway service");
  
          afsi_log("Using >%s< as SMB server name", cm_NetbiosName);
--- 798,815 ----
                             (BYTE *) &HardDeadtimeout, &dummyLen);
      afsi_log("HardDeadTimeout is %d", HardDeadtimeout);
  
!     RegCloseKey (parmKey);
  
      /* Call lanahelper to get Netbios name, lan adapter number and gateway flag */
      if(SUCCEEDED(code = lana_GetUncServerNameEx(cm_NetbiosName, &lanaNum, &isGateway, LANA_NETBIOS_NAME_FULL))) {
          LANadapter = (lanaNum == LANA_INVALID)? -1: lanaNum;
  
!         if (LANadapter != -1)
              afsi_log("LAN adapter number %d", LANadapter);
          else
              afsi_log("LAN adapter number not determined");
  
!         if (isGateway)
              afsi_log("Set for gateway service");
  
          afsi_log("Using >%s< as SMB server name", cm_NetbiosName);
***************
*** 797,834 ****
          osi_panic(buf, __FILE__, __LINE__);
      }
  
! 	/* setup early variables */
! 	/* These both used to be configurable. */
! 	smb_UseV3 = 1;
      buf_bufferSize = CM_CONFIGDEFAULT_BLOCKSIZE;
  
! 	/* turn from 1024 byte units into memory blocks */
      cacheBlocks = (cacheSize * 1024) / buf_bufferSize;
          
! 	/* get network related info */
! 	cm_noIPAddr = CM_MAXINTERFACE_ADDR;
! 	code = syscfg_GetIFInfo(&cm_noIPAddr,
!                             cm_IPAddr, cm_SubnetMask,
!                             cm_NetMtu, cm_NetFlags);
  
! 	if ( (cm_noIPAddr <= 0) || (code <= 0 ) )
! 	    afsi_log("syscfg_GetIFInfo error code %d", code);
! 	else
! 	    afsi_log("First Network address %x SubnetMask %x",
!                  cm_IPAddr[0], cm_SubnetMask[0]);
  
! 	/*
! 	 * Save client configuration for GetCacheConfig requests
! 	 */
! 	cm_initParams.nChunkFiles = 0;
! 	cm_initParams.nStatCaches = stats;
! 	cm_initParams.nDataCaches = 0;
! 	cm_initParams.nVolumeCaches = 0;
! 	cm_initParams.firstChunkSize = cm_chunkSize;
! 	cm_initParams.otherChunkSize = cm_chunkSize;
! 	cm_initParams.cacheSize = cacheSize;
! 	cm_initParams.setTime = 0;
! 	cm_initParams.memCache = 0;
  
      /* Set RX parameters before initializing RX */
      if ( rx_nojumbo ) {
--- 819,856 ----
          osi_panic(buf, __FILE__, __LINE__);
      }
  
!     /* setup early variables */
!     /* These both used to be configurable. */
!     smb_UseV3 = 1;
      buf_bufferSize = CM_CONFIGDEFAULT_BLOCKSIZE;
  
!     /* turn from 1024 byte units into memory blocks */
      cacheBlocks = (cacheSize * 1024) / buf_bufferSize;
          
!     /* get network related info */
!     cm_noIPAddr = CM_MAXINTERFACE_ADDR;
!     code = syscfg_GetIFInfo(&cm_noIPAddr,
!                              cm_IPAddr, cm_SubnetMask,
!                              cm_NetMtu, cm_NetFlags);
  
!     if ( (cm_noIPAddr <= 0) || (code <= 0 ) )
!         afsi_log("syscfg_GetIFInfo error code %d", code);
!     else
!         afsi_log("First Network address %x SubnetMask %x",
!                   cm_IPAddr[0], cm_SubnetMask[0]);
  
!     /*
!      * Save client configuration for GetCacheConfig requests
!      */
!     cm_initParams.nChunkFiles = 0;
!     cm_initParams.nStatCaches = stats;
!     cm_initParams.nDataCaches = 0;
!     cm_initParams.nVolumeCaches = 0;
!     cm_initParams.firstChunkSize = cm_chunkSize;
!     cm_initParams.otherChunkSize = cm_chunkSize;
!     cm_initParams.cacheSize = cacheSize;
!     cm_initParams.setTime = 0;
!     cm_initParams.memCache = 0;
  
      /* Set RX parameters before initializing RX */
      if ( rx_nojumbo ) {
***************
*** 847,894 ****
      /* Ensure the AFS Netbios Name is registered to allow loopback access */
      configureBackConnectionHostNames();
  
! 	/* initialize RX, and tell it to listen to port 7001, which is used for
       * callback RPC messages.
       */
! 	code = rx_Init(htons(7001));
! 	afsi_log("rx_Init code %x", code);
! 	if (code != 0) {
! 		*reasonP = "afsd: failed to init rx client on port 7001";
! 		return -1;
! 	}
  
! 	/* Initialize the RPC server for session keys */
! 	RpcInit();
  
! 	/* create an unauthenticated service #1 for callbacks */
! 	nullServerSecurityClassp = rxnull_NewServerSecurityObject();
      serverp = rx_NewService(0, 1, "AFS", &nullServerSecurityClassp, 1,
!                             RXAFSCB_ExecuteRequest);
! 	afsi_log("rx_NewService addr %x", (int)serverp);
! 	if (serverp == NULL) {
! 		*reasonP = "unknown error";
! 		return -1;
! 	}
  
! 	nullServerSecurityClassp = rxnull_NewServerSecurityObject();
      serverp = rx_NewService(0, RX_STATS_SERVICE_ID, "rpcstats",
!                             &nullServerSecurityClassp, 1, RXSTATS_ExecuteRequest);
! 	afsi_log("rx_NewService addr %x", (int)serverp);
! 	if (serverp == NULL) {
! 		*reasonP = "unknown error";
! 		return -1;
! 	}
          
      /* start server threads, *not* donating this one to the pool */
      rx_StartServer(0);
! 	afsi_log("rx_StartServer");
  
! 	/* init user daemon, and other packages */
! 	cm_InitUser();
  
! 	cm_InitACLCache(2*stats);
  
! 	cm_InitConn();
  
      cm_InitCell();
          
--- 869,916 ----
      /* Ensure the AFS Netbios Name is registered to allow loopback access */
      configureBackConnectionHostNames();
  
!     /* initialize RX, and tell it to listen to port 7001, which is used for
       * callback RPC messages.
       */
!     code = rx_Init(htons(7001));
!     afsi_log("rx_Init code %x", code);
!     if (code != 0) {
!         *reasonP = "afsd: failed to init rx client on port 7001";
!         return -1;
!     }
  
!     /* Initialize the RPC server for session keys */
!     RpcInit();
  
!     /* create an unauthenticated service #1 for callbacks */
!     nullServerSecurityClassp = rxnull_NewServerSecurityObject();
      serverp = rx_NewService(0, 1, "AFS", &nullServerSecurityClassp, 1,
!                              RXAFSCB_ExecuteRequest);
!     afsi_log("rx_NewService addr %x", (int)serverp);
!     if (serverp == NULL) {
!         *reasonP = "unknown error";
!         return -1;
!     }
  
!     nullServerSecurityClassp = rxnull_NewServerSecurityObject();
      serverp = rx_NewService(0, RX_STATS_SERVICE_ID, "rpcstats",
!                              &nullServerSecurityClassp, 1, RXSTATS_ExecuteRequest);
!     afsi_log("rx_NewService addr %x", (int)serverp);
!     if (serverp == NULL) {
!         *reasonP = "unknown error";
!         return -1;
!     }
          
      /* start server threads, *not* donating this one to the pool */
      rx_StartServer(0);
!     afsi_log("rx_StartServer");
  
!     /* init user daemon, and other packages */
!     cm_InitUser();
  
!     cm_InitACLCache(2*stats);
  
!     cm_InitConn();
  
      cm_InitCell();
          
***************
*** 905,932 ****
      cm_InitSCache(stats);
          
      code = cm_InitDCache(0, cacheBlocks);
! 	afsi_log("cm_InitDCache code %x", code);
! 	if (code != 0) {
! 		*reasonP = "error initializing cache";
! 		return -1;
! 	}
  
  #ifdef AFS_AFSDB_ENV
  #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
! 	if (cm_InitDNS(cm_dnsEnabled) == -1)
! 	  cm_dnsEnabled = 0;  /* init failed, so deactivate */
! 	afsi_log("cm_InitDNS %d", cm_dnsEnabled);
  #endif
  #endif
  
! 	code = cm_GetRootCellName(rootCellName);
! 	afsi_log("cm_GetRootCellName code %d, cm_freelanceEnabled= %d, rcn= %s", 
!              code, cm_freelanceEnabled, (code ? "<none>" : rootCellName));
! 	if (code != 0 && !cm_freelanceEnabled) 
      {
! 	    *reasonP = "can't find root cell name in afsd.ini";
! 	    return -1;
!     }
      else if (cm_freelanceEnabled)
          cm_rootCellp = NULL;
  
--- 927,954 ----
      cm_InitSCache(stats);
          
      code = cm_InitDCache(0, cacheBlocks);
!     afsi_log("cm_InitDCache code %x", code);
!     if (code != 0) {
!         *reasonP = "error initializing cache";
!         return -1;
!     }
  
  #ifdef AFS_AFSDB_ENV
  #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
!     if (cm_InitDNS(cm_dnsEnabled) == -1)
!         cm_dnsEnabled = 0;  /* init failed, so deactivate */
!     afsi_log("cm_InitDNS %d", cm_dnsEnabled);
  #endif
  #endif
  
!     code = cm_GetRootCellName(rootCellName);
!     afsi_log("cm_GetRootCellName code %d, cm_freelanceEnabled= %d, rcn= %s", 
!               code, cm_freelanceEnabled, (code ? "<none>" : rootCellName));
!     if (code != 0 && !cm_freelanceEnabled) 
      {
!         *reasonP = "can't find root cell name in afsd.ini";
!         return -1;
!     }   
      else if (cm_freelanceEnabled)
          cm_rootCellp = NULL;
  
***************
*** 939,952 ****
              *reasonP = "can't find root cell in afsdcell.ini";
              return -1;
          }
! 	}
! 
  
  #ifdef AFS_FREELANCE_CLIENT
! 	if (cm_freelanceEnabled)
! 	  cm_InitFreelance();
  #endif
! 	return 0;
  }
  
  int afsd_InitDaemons(char **reasonP)
--- 961,973 ----
              *reasonP = "can't find root cell in afsdcell.ini";
              return -1;
          }
!     }
  
  #ifdef AFS_FREELANCE_CLIENT
!     if (cm_freelanceEnabled)
!         cm_InitFreelance();
  #endif
!     return 0;
  }
  
  int afsd_InitDaemons(char **reasonP)
Index: openafs/src/WINNT/afsd/afsd_service.c
diff -c openafs/src/WINNT/afsd/afsd_service.c:1.28 openafs/src/WINNT/afsd/afsd_service.c:1.28.2.2
*** openafs/src/WINNT/afsd/afsd_service.c:1.28	Sat Jul 24 11:25:35 2004
--- openafs/src/WINNT/afsd/afsd_service.c	Mon Oct 18 00:09:25 2004
***************
*** 32,38 ****
  // The following is defined if you want to receive Power notifications,
  // including Hibernation, and also subsequent flushing of AFS volumes
  //
! #define REGISTER_POWER_NOTIFICATIONS
  //
  // Check
  */
--- 32,39 ----
  // The following is defined if you want to receive Power notifications,
  // including Hibernation, and also subsequent flushing of AFS volumes
  //
! #define REGISTER_POWER_NOTIFICATIONS 1
! #define FLUSH_VOLUME                 1
  //
  // Check
  */
***************
*** 54,84 ****
  extern int traceOnPanic;
  extern HANDLE afsi_file;
  
  /*
   * Notifier function for use by osi_panic
   */
  static void afsd_notifier(char *msgp, char *filep, long line)
  {
! 	char tbuffer[512];
! 	char *ptbuf[1];
! 	HANDLE h;
! 
! 	if (filep)
! 		sprintf(tbuffer, "Error at file %s, line %d: %s",
! 			filep, line, msgp);
! 	else
! 		sprintf(tbuffer, "Error at unknown location: %s", msgp);
  
! 	h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
! 	ptbuf[0] = tbuffer;
! 	ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, line, NULL, 1, 0, ptbuf, NULL);
! 	DeregisterEventSource(h);
  
! 	GlobalStatus = line;
  
! 	osi_LogEnable(afsd_logp);
  
! 	afsd_ForceTrace(TRUE);
  
      afsi_log("--- begin dump ---");
      cm_DumpSCache(afsi_file, "a");
--- 55,87 ----
  extern int traceOnPanic;
  extern HANDLE afsi_file;
  
+ int powerEventsRegistered = 0;
+ 
  /*
   * Notifier function for use by osi_panic
   */
  static void afsd_notifier(char *msgp, char *filep, long line)
  {
!     char tbuffer[512];
!     char *ptbuf[1];
!     HANDLE h;
! 
!     if (filep)
!         sprintf(tbuffer, "Error at file %s, line %d: %s",
!                  filep, line, msgp);
!     else
!         sprintf(tbuffer, "Error at unknown location: %s", msgp);
  
!     h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
!     ptbuf[0] = tbuffer;
!     ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, line, NULL, 1, 0, ptbuf, NULL);
!     DeregisterEventSource(h);
  
!     GlobalStatus = line;
  
!     osi_LogEnable(afsd_logp);
  
!     afsd_ForceTrace(TRUE);
  
      afsi_log("--- begin dump ---");
      cm_DumpSCache(afsi_file, "a");
***************
*** 91,104 ****
      
      DebugBreak();	
  
! 	SetEvent(WaitToTerminate);
  
  #ifdef JUMP
! 	if (GetCurrentThreadId() == MainThreadId)
! 		longjmp(notifier_jmp, 1);
! 	else
  #endif /* JUMP */
! 		ExitThread(1);
  }
  
  /*
--- 94,107 ----
      
      DebugBreak();	
  
!     SetEvent(WaitToTerminate);
  
  #ifdef JUMP
!     if (GetCurrentThreadId() == MainThreadId)
!         longjmp(notifier_jmp, 1);
!     else
  #endif /* JUMP */
!         ExitThread(1);
  }
  
  /*
***************
*** 106,112 ****
   */
  static int _stdcall DummyMessageBox(HWND h, LPCTSTR l1, LPCTSTR l2, UINT ui)
  {
! 	return 0;
  }
  
  static SERVICE_STATUS		ServiceStatus;
--- 109,115 ----
   */
  static int _stdcall DummyMessageBox(HWND h, LPCTSTR l1, LPCTSTR l2, UINT ui)
  {
!     return 0;
  }
  
  static SERVICE_STATUS		ServiceStatus;
***************
*** 130,136 ****
      {
          dwRet = NO_ERROR;
      }
- 
      else
      {
          /* flush was unsuccessful, or timeout - deny shutdown */
--- 133,138 ----
***************
*** 151,203 ****
  VOID WINAPI 
  afsd_ServiceControlHandler(DWORD ctrlCode)
  {
! 	HKEY parmKey;
! 	DWORD dummyLen, doTrace;
! 	long code;
! 
! 	switch (ctrlCode) {
! 		case SERVICE_CONTROL_STOP:
! 			/* Shutdown RPC */
! 			RpcMgmtStopServerListening(NULL);
! 
! 			/* Force trace if requested */
! 			code = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
! 					    AFSConfigKeyName,
! 					    0, KEY_QUERY_VALUE, &parmKey);
! 			if (code != ERROR_SUCCESS)
! 				goto doneTrace;
! 
! 			dummyLen = sizeof(doTrace);
! 			code = RegQueryValueEx(parmKey, "TraceOnShutdown",
! 						NULL, NULL,
! 						(BYTE *) &doTrace, &dummyLen);
! 			RegCloseKey (parmKey);
! 			if (code != ERROR_SUCCESS)
! 				doTrace = 0;
! 			if (doTrace)
! 				afsd_ForceTrace(FALSE);
! 
! doneTrace:
! 			ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
! 			ServiceStatus.dwWin32ExitCode = NO_ERROR;
! 			ServiceStatus.dwCheckPoint = 1;
! 			ServiceStatus.dwWaitHint = 10000;
! 			ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
! 			SetServiceStatus(StatusHandle, &ServiceStatus);
! 			SetEvent(WaitToTerminate);
! 			break;
! 		case SERVICE_CONTROL_INTERROGATE:
! 			ServiceStatus.dwCurrentState = SERVICE_RUNNING;
! 			ServiceStatus.dwWin32ExitCode = NO_ERROR;
! 			ServiceStatus.dwCheckPoint = 0;
! 			ServiceStatus.dwWaitHint = 0;
! 			ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
! 			SetServiceStatus(StatusHandle, &ServiceStatus);
! 			break;
! 		/* XXX handle system shutdown */
! 		/* XXX handle pause & continue */
! 	}
! }
  
  
  /*
--- 153,205 ----
  VOID WINAPI 
  afsd_ServiceControlHandler(DWORD ctrlCode)
  {
!     HKEY parmKey;
!     DWORD dummyLen, doTrace;
!     long code;
! 
!     switch (ctrlCode) {
!     case SERVICE_CONTROL_STOP:
!         /* Shutdown RPC */
!         RpcMgmtStopServerListening(NULL);
! 
!         /* Force trace if requested */
!         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
!                              AFSConfigKeyName,
!                              0, KEY_QUERY_VALUE, &parmKey);
!         if (code != ERROR_SUCCESS)
!             goto doneTrace;
! 
!         dummyLen = sizeof(doTrace);
!         code = RegQueryValueEx(parmKey, "TraceOnShutdown",
!                                 NULL, NULL,
!                                 (BYTE *) &doTrace, &dummyLen);
!         RegCloseKey (parmKey);
!         if (code != ERROR_SUCCESS)
!             doTrace = 0;
!         if (doTrace)
!             afsd_ForceTrace(FALSE);
! 
!       doneTrace:
!         ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
!         ServiceStatus.dwWin32ExitCode = NO_ERROR;
!         ServiceStatus.dwCheckPoint = 1;
!         ServiceStatus.dwWaitHint = 10000;
!         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
!         SetServiceStatus(StatusHandle, &ServiceStatus);
!         SetEvent(WaitToTerminate);
!         break;
!     case SERVICE_CONTROL_INTERROGATE:
!         ServiceStatus.dwCurrentState = SERVICE_RUNNING;
!         ServiceStatus.dwWin32ExitCode = NO_ERROR;
!         ServiceStatus.dwCheckPoint = 0;
!         ServiceStatus.dwWaitHint = 0;
!         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
!         SetServiceStatus(StatusHandle, &ServiceStatus);
!         break;
!         /* XXX handle system shutdown */
!         /* XXX handle pause & continue */
!     }
! }       
  
  
  /*
***************
*** 212,223 ****
                LPVOID lpContext
                )
  {
! 	HKEY parmKey;
! 	DWORD dummyLen, doTrace;
! 	long code;
      DWORD dwRet = ERROR_CALL_NOT_IMPLEMENTED;
  
! 	switch (ctrlCode) 
      {
      case SERVICE_CONTROL_STOP:
          /* Shutdown RPC */
--- 214,225 ----
                LPVOID lpContext
                )
  {
!     HKEY parmKey;
!     DWORD dummyLen, doTrace;
!     long code;
      DWORD dwRet = ERROR_CALL_NOT_IMPLEMENTED;
  
!     switch (ctrlCode) 
      {
      case SERVICE_CONTROL_STOP:
          /* Shutdown RPC */
***************
*** 261,305 ****
          dwRet = NO_ERROR;
          break;
  
! 		/* XXX handle system shutdown */
! 		/* XXX handle pause & continue */
! 		case SERVICE_CONTROL_POWEREVENT:                                              
! 		{                                                                                     
! 			/*                                                                                
              **	dwEventType of this notification == WPARAM of WM_POWERBROADCAST               
! 			**	Return NO_ERROR == return TRUE for that message, i.e. accept request          
! 			**	Return any error code to deny request,                                        
! 			**	i.e. as if returning BROADCAST_QUERY_DENY                                     
! 			*/                                                                                
! 			switch((int) dwEventType)                                                         
!             {                                                                               
! 			case PBT_APMQUERYSUSPEND:                                                         
! 			case PBT_APMQUERYSTANDBY:                                                         
!                                                                                             
! #ifdef	REGISTER_POWER_NOTIFICATIONS				                                      
! 				/* handle event */                                                            
! 				dwRet = afsd_ServiceFlushVolume((DWORD) lpEventData);                         
  #else                                                                                       
! 				dwRet = NO_ERROR;                                                             
  #endif                                                                                      
! 				break;                                                                        
  							                                                                  
!             /* allow remaining case PBT_WhatEver */                                           
! 			case PBT_APMSUSPEND:                                                              
! 			case PBT_APMSTANDBY:                                                              
! 			case PBT_APMRESUMECRITICAL:                                                       
! 			case PBT_APMRESUMESUSPEND:                                                        
! 			case PBT_APMRESUMESTANDBY:                                                        
! 			case PBT_APMBATTERYLOW:                                                           
! 			case PBT_APMPOWERSTATUSCHANGE:                                                    
! 			case PBT_APMOEMEVENT:                                                             
! 			case PBT_APMRESUMEAUTOMATIC:                                                      
! 			default:                                                                          
! 				dwRet = NO_ERROR;                                                             
              }
          }
      }		/* end switch(ctrlCode) */                                                        
! 	return dwRet;   
  }
  
  /* There is similar code in client_config\drivemap.cpp GlobalMountDrive()
--- 263,309 ----
          dwRet = NO_ERROR;
          break;
  
!         /* XXX handle system shutdown */
!         /* XXX handle pause & continue */
!     case SERVICE_CONTROL_POWEREVENT:                                              
!         {                                                                                     
!             /*                                                                                
              **	dwEventType of this notification == WPARAM of WM_POWERBROADCAST               
!             **	Return NO_ERROR == return TRUE for that message, i.e. accept request          
!             **	Return any error code to deny request,                                        
!             **	i.e. as if returning BROADCAST_QUERY_DENY                                     
!             */                                                                                
!             if (powerEventsRegistered) {
!                 switch((int) dwEventType)                                                         
!                 {                                                                               
!                 case PBT_APMQUERYSUSPEND:                                                         
!                 case PBT_APMQUERYSTANDBY:                                                         
! 
! #ifdef	FLUSH_VOLUME
!                     /* handle event */                                                            
!                     dwRet = afsd_ServiceFlushVolume((DWORD) lpEventData);                         
  #else                                                                                       
!                     dwRet = NO_ERROR;                                                             
  #endif                                                                                      
!                     break;                                                                        
  							                                                                  
!                     /* allow remaining case PBT_WhatEver */                                           
!                 case PBT_APMSUSPEND:                                                              
!                 case PBT_APMSTANDBY:                                                              
!                 case PBT_APMRESUMECRITICAL:                                                       
!                 case PBT_APMRESUMESUSPEND:                                                        
!                 case PBT_APMRESUMESTANDBY:                                                        
!                 case PBT_APMBATTERYLOW:                                                           
!                 case PBT_APMPOWERSTATUSCHANGE:                                                    
!                 case PBT_APMOEMEVENT:                                                             
!                 case PBT_APMRESUMEAUTOMATIC:                                                      
!                 default:                                                                          
!                     dwRet = NO_ERROR;                                                             
!                 }   
              }
          }
      }		/* end switch(ctrlCode) */                                                        
!     return dwRet;   
  }
  
  /* There is similar code in client_config\drivemap.cpp GlobalMountDrive()
***************
*** 308,314 ****
   */
  /* DEE Could check first if we are run as SYSTEM */
  #define MAX_RETRIES 30
! static void MountGlobalDrives()
  {
      char szAfsPath[_MAX_PATH];
      char szDriveToMapTo[5];
--- 312,318 ----
   */
  /* DEE Could check first if we are run as SYSTEM */
  #define MAX_RETRIES 30
! static void MountGlobalDrives(void)
  {
      char szAfsPath[_MAX_PATH];
      char szDriveToMapTo[5];
***************
*** 323,330 ****
  
      sprintf(szKeyName, "%s\\GlobalAutoMapper", AFSConfigKeyName);
  
! 	dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_QUERY_VALUE, &hKey);
! 	if (dwResult != ERROR_SUCCESS)
          return;
  
      while (dwRetry < MAX_RETRIES) {
--- 327,334 ----
  
      sprintf(szKeyName, "%s\\GlobalAutoMapper", AFSConfigKeyName);
  
!     dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_QUERY_VALUE, &hKey);
!     if (dwResult != ERROR_SUCCESS)
          return;
  
      while (dwRetry < MAX_RETRIES) {
***************
*** 385,392 ****
  
      sprintf(szKeyName, "%s\\GlobalAutoMapper", AFSConfigKeyName);
  
! 	dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_QUERY_VALUE, &hKey);
! 	if (dwResult != ERROR_SUCCESS)
          return;
  
      while (1) {
--- 389,396 ----
  
      sprintf(szKeyName, "%s\\GlobalAutoMapper", AFSConfigKeyName);
  
!     dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_QUERY_VALUE, &hKey);
!     if (dwResult != ERROR_SUCCESS)
          return;
  
      while (1) {
***************
*** 428,437 ****
  
  void afsd_Main(DWORD argc, LPTSTR *argv)
  {
! 	long code;
! 	char *reason;
  #ifdef JUMP
! 	int jmpret;
  #endif /* JUMP */
      HANDLE hInitHookDll;
      HANDLE hAdvApi32;
--- 432,441 ----
  
  void afsd_Main(DWORD argc, LPTSTR *argv)
  {
!     long code;
!     char *reason;
  #ifdef JUMP
!     int jmpret;
  #endif /* JUMP */
      HANDLE hInitHookDll;
      HANDLE hAdvApi32;
***************
*** 443,455 ****
  #endif 
  
      osi_InitPanic(afsd_notifier);
! 	osi_InitTraceOption();
  
! 	GlobalStatus = 0;
  
! 	afsi_start();
  
! 	WaitToTerminate = CreateEvent(NULL, TRUE, FALSE, TEXT("afsd_service_WaitToTerminate"));
      if ( GetLastError() == ERROR_ALREADY_EXISTS )
          afsi_log("Event Object Already Exists: %s", TEXT("afsd_service_WaitToTerminate"));
  
--- 447,459 ----
  #endif 
  
      osi_InitPanic(afsd_notifier);
!     osi_InitTraceOption();
  
!     GlobalStatus = 0;
  
!     afsi_start();
  
!     WaitToTerminate = CreateEvent(NULL, TRUE, FALSE, TEXT("afsd_service_WaitToTerminate"));
      if ( GetLastError() == ERROR_ALREADY_EXISTS )
          afsi_log("Event Object Already Exists: %s", TEXT("afsd_service_WaitToTerminate"));
  
***************
*** 472,486 ****
          StatusHandle = RegisterServiceCtrlHandler(AFS_DAEMON_SERVICE_NAME, afsd_ServiceControlHandler);
      }
  
! 	ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
! 	ServiceStatus.dwServiceSpecificExitCode = 0;
! 	ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
! 	ServiceStatus.dwWin32ExitCode = NO_ERROR;
! 	ServiceStatus.dwCheckPoint = 1;
! 	ServiceStatus.dwWaitHint = 30000;
      /* accept Power Events */
! 	ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT;
! 	SetServiceStatus(StatusHandle, &ServiceStatus);
  #endif
  
      {       
--- 476,490 ----
          StatusHandle = RegisterServiceCtrlHandler(AFS_DAEMON_SERVICE_NAME, afsd_ServiceControlHandler);
      }
  
!     ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
!     ServiceStatus.dwServiceSpecificExitCode = 0;
!     ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
!     ServiceStatus.dwWin32ExitCode = NO_ERROR;
!     ServiceStatus.dwCheckPoint = 1;
!     ServiceStatus.dwWaitHint = 30000;
      /* accept Power Events */
!     ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT;
!     SetServiceStatus(StatusHandle, &ServiceStatus);
  #endif
  
      {       
***************
*** 492,499 ****
      }
  
  #ifdef REGISTER_POWER_NOTIFICATIONS
!     /* create thread used to flush cache */
!     PowerNotificationThreadCreate();
  #endif
  
      /* allow an exit to be called prior to any initialization */
--- 496,525 ----
      }
  
  #ifdef REGISTER_POWER_NOTIFICATIONS
!     {
!         HKEY hkParm;
!         DWORD code;
!         DWORD dummyLen;
!         int bpower = TRUE;
! 
!         /* see if we should handle power notifications */
!         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSConfigKeyName, 0, KEY_QUERY_VALUE, &hkParm);
!         if (code == ERROR_SUCCESS) {
!             dummyLen = sizeof(bpower);
!             code = RegQueryValueEx(hkParm, "FlushOnHibernate", NULL, NULL,
!                 (BYTE *) &bpower, &dummyLen);      
! 
!             if(code != ERROR_SUCCESS)
!                 bpower = TRUE;
! 
! 	    RegCloseKey(hkParm);
!         }
!         /* create thread used to flush cache */
!         if (bpower) {
!             PowerNotificationThreadCreate();
!             powerEventsRegistered = 1;
!         }
!     }
  #endif
  
      /* allow an exit to be called prior to any initialization */
***************
*** 538,552 ****
  
  #ifdef JUMP
      MainThreadId = GetCurrentThreadId();
! 	jmpret = setjmp(notifier_jmp);
  
! 	if (jmpret == 0) 
  #endif /* JUMP */
      {
! 		code = afsd_InitCM(&reason);
! 		if (code != 0) {
              afsi_log("afsd_InitCM failed: %s (code = %d)", reason, code);
! 			osi_panic(reason, __FILE__, __LINE__);
          }
  
  #ifndef NOTSERVICE
--- 564,578 ----
  
  #ifdef JUMP
      MainThreadId = GetCurrentThreadId();
!     jmpret = setjmp(notifier_jmp);
  
!     if (jmpret == 0) 
  #endif /* JUMP */
      {
!         code = afsd_InitCM(&reason);
!         if (code != 0) {
              afsi_log("afsd_InitCM failed: %s (code = %d)", reason, code);
!             osi_panic(reason, __FILE__, __LINE__);
          }
  
  #ifndef NOTSERVICE
***************
*** 554,561 ****
          ServiceStatus.dwWaitHint -= 5000;
          SetServiceStatus(StatusHandle, &ServiceStatus);
  #endif
! 		code = afsd_InitDaemons(&reason);
! 		if (code != 0) {
              afsi_log("afsd_InitDaemons failed: %s (code = %d)", reason, code);
  			osi_panic(reason, __FILE__, __LINE__);
          }
--- 580,587 ----
          ServiceStatus.dwWaitHint -= 5000;
          SetServiceStatus(StatusHandle, &ServiceStatus);
  #endif
!         code = afsd_InitDaemons(&reason);
!         if (code != 0) {
              afsi_log("afsd_InitDaemons failed: %s (code = %d)", reason, code);
  			osi_panic(reason, __FILE__, __LINE__);
          }
***************
*** 565,606 ****
          ServiceStatus.dwWaitHint -= 5000;
          SetServiceStatus(StatusHandle, &ServiceStatus);
  #endif
! 		code = afsd_InitSMB(&reason, MessageBox);
! 		if (code != 0) {
              afsi_log("afsd_InitSMB failed: %s (code = %d)", reason, code);
! 			osi_panic(reason, __FILE__, __LINE__);
          }
  
          MountGlobalDrives();
  
  #ifndef NOTSERVICE
! 		ServiceStatus.dwCurrentState = SERVICE_RUNNING;
! 		ServiceStatus.dwWin32ExitCode = NO_ERROR;
! 		ServiceStatus.dwCheckPoint = 0;
! 		ServiceStatus.dwWaitHint = 0;
  
          /* accept Power events */
! 		ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_POWEREVENT;
! 		SetServiceStatus(StatusHandle, &ServiceStatus);
! #endif
          {
  	    HANDLE h; char *ptbuf[1];
! 		h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
! 		ptbuf[0] = "AFS running";
! 		ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, ptbuf, NULL);
! 		DeregisterEventSource(h);
          }
! 	}
  
! 	WaitForSingleObject(WaitToTerminate, INFINITE);
  
      {   
!     HANDLE h; char *ptbuf[1];
  	h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
  	ptbuf[0] = "AFS quitting";
  	ReportEvent(h, GlobalStatus ? EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE,
                  0, 0, NULL, 1, 0, ptbuf, NULL);
!     DeregisterEventSource(h);
      }
  
      DismountGlobalDrives();
--- 591,632 ----
          ServiceStatus.dwWaitHint -= 5000;
          SetServiceStatus(StatusHandle, &ServiceStatus);
  #endif
!         code = afsd_InitSMB(&reason, MessageBox);
!         if (code != 0) {
              afsi_log("afsd_InitSMB failed: %s (code = %d)", reason, code);
!             osi_panic(reason, __FILE__, __LINE__);
          }
  
          MountGlobalDrives();
  
  #ifndef NOTSERVICE
!         ServiceStatus.dwCurrentState = SERVICE_RUNNING;
!         ServiceStatus.dwWin32ExitCode = NO_ERROR;
!         ServiceStatus.dwCheckPoint = 0;
!         ServiceStatus.dwWaitHint = 0;
  
          /* accept Power events */
!         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_POWEREVENT;
!         SetServiceStatus(StatusHandle, &ServiceStatus);
! #endif  
          {
  	    HANDLE h; char *ptbuf[1];
!             h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
!             ptbuf[0] = "AFS running";
!             ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, ptbuf, NULL);
!             DeregisterEventSource(h);
          }
!     }
  
!     WaitForSingleObject(WaitToTerminate, INFINITE);
  
      {   
!         HANDLE h; char *ptbuf[1];
  	h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
  	ptbuf[0] = "AFS quitting";
  	ReportEvent(h, GlobalStatus ? EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE,
                  0, 0, NULL, 1, 0, ptbuf, NULL);
!         DeregisterEventSource(h);
      }
  
      DismountGlobalDrives();
***************
*** 608,627 ****
      rx_Finalize();
  
  #ifdef	REGISTER_POWER_NOTIFICATIONS
! 	/* terminate thread used to flush cache */
! 	PowerNotificationThreadExit();
  #endif
  
      /* Remove the ExceptionFilter */
      SetUnhandledExceptionFilter(NULL);
  
      ServiceStatus.dwCurrentState = SERVICE_STOPPED;
! 	ServiceStatus.dwWin32ExitCode = GlobalStatus ? ERROR_EXCEPTION_IN_SERVICE : NO_ERROR;
! 	ServiceStatus.dwCheckPoint = 0;
! 	ServiceStatus.dwWaitHint = 0;
! 	ServiceStatus.dwControlsAccepted = 0;
! 	SetServiceStatus(StatusHandle, &ServiceStatus);
! }
  
  DWORD __stdcall afsdMain_thread(void* notUsed)
  {
--- 634,654 ----
      rx_Finalize();
  
  #ifdef	REGISTER_POWER_NOTIFICATIONS
!     /* terminate thread used to flush cache */
!     if (powerEventsRegistered)
!         PowerNotificationThreadExit();
  #endif
  
      /* Remove the ExceptionFilter */
      SetUnhandledExceptionFilter(NULL);
  
      ServiceStatus.dwCurrentState = SERVICE_STOPPED;
!     ServiceStatus.dwWin32ExitCode = GlobalStatus ? ERROR_EXCEPTION_IN_SERVICE : NO_ERROR;
!     ServiceStatus.dwCheckPoint = 0;
!     ServiceStatus.dwWaitHint = 0;
!     ServiceStatus.dwControlsAccepted = 0;
!     SetServiceStatus(StatusHandle, &ServiceStatus);
! }       
  
  DWORD __stdcall afsdMain_thread(void* notUsed)
  {
***************
*** 633,647 ****
  int
  main(void)
  {
! 	static SERVICE_TABLE_ENTRY dispatchTable[] = {
! 		{AFS_DAEMON_SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) afsd_Main},
! 		{NULL, NULL}
! 	};
  
! 	if (!StartServiceCtrlDispatcher(dispatchTable))
      {
          LONG status = GetLastError();
! 	    if (status == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
          {
              DWORD tid;
              hAFSDMainThread = CreateThread(NULL, 0, afsdMain_thread, 0, 0, &tid);
--- 660,674 ----
  int
  main(void)
  {
!     static SERVICE_TABLE_ENTRY dispatchTable[] = {
!         {AFS_DAEMON_SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) afsd_Main},
!         {NULL, NULL}
!     };
  
!     if (!StartServiceCtrlDispatcher(dispatchTable))
      {
          LONG status = GetLastError();
!         if (status == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
          {
              DWORD tid;
              hAFSDMainThread = CreateThread(NULL, 0, afsdMain_thread, 0, 0, &tid);
Index: openafs/src/WINNT/afsd/afskfw.c
diff -c openafs/src/WINNT/afsd/afskfw.c:1.8.2.1 openafs/src/WINNT/afsd/afskfw.c:1.8.2.4
*** openafs/src/WINNT/afsd/afskfw.c:1.8.2.1	Tue Aug 17 00:28:38 2004
--- openafs/src/WINNT/afsd/afskfw.c	Mon Oct 18 00:09:25 2004
***************
*** 444,471 ****
  
  static char OpenAFSConfigKeyName[] = "SOFTWARE\\OpenAFS\\Client";
  
  int 
  KFW_is_available(void)
  {
      HKEY parmKey;
! 	DWORD code, len;
      DWORD enableKFW = 1;
  
      code = RegOpenKeyEx(HKEY_CURRENT_USER, OpenAFSConfigKeyName,
                           0, KEY_QUERY_VALUE, &parmKey);
!     if (code != ERROR_SUCCESS)
!         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, OpenAFSConfigKeyName,
!                              0, KEY_QUERY_VALUE, &parmKey);
! 	if (code == ERROR_SUCCESS) {
          len = sizeof(enableKFW);
          code = RegQueryValueEx(parmKey, "EnableKFW", NULL, NULL,
                                  (BYTE *) &enableKFW, &len);
          if (code != ERROR_SUCCESS) {
!             enableKFW = 1;
          }
          RegCloseKey (parmKey);
! 	}
! 
      if ( !enableKFW )
          return FALSE;
  
--- 444,508 ----
  
  static char OpenAFSConfigKeyName[] = "SOFTWARE\\OpenAFS\\Client";
  
+ int
+ KFW_use_krb524(void)
+ {
+     HKEY parmKey;
+     DWORD code, len;
+     DWORD use524 = 0;
+ 
+     code = RegOpenKeyEx(HKEY_CURRENT_USER, OpenAFSConfigKeyName,
+                          0, KEY_QUERY_VALUE, &parmKey);
+     if (code == ERROR_SUCCESS) {
+         len = sizeof(use524);
+         code = RegQueryValueEx(parmKey, "Use524", NULL, NULL,
+                                 (BYTE *) &use524, &len);
+         if (code != ERROR_SUCCESS) {
+             RegCloseKey(parmKey);
+ 
+             code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, OpenAFSConfigKeyName,
+                                  0, KEY_QUERY_VALUE, &parmKey);
+             if (code == ERROR_SUCCESS) {
+                 len = sizeof(use524);
+                 code = RegQueryValueEx(parmKey, "Use524", NULL, NULL,
+                                         (BYTE *) &use524, &len);
+                 if (code != ERROR_SUCCESS)
+                     use524 = 0;
+             }
+         }
+         RegCloseKey (parmKey);
+     }
+     return use524;
+ }
+ 
  int 
  KFW_is_available(void)
  {
      HKEY parmKey;
!     DWORD code, len;
      DWORD enableKFW = 1;
  
      code = RegOpenKeyEx(HKEY_CURRENT_USER, OpenAFSConfigKeyName,
                           0, KEY_QUERY_VALUE, &parmKey);
!     if (code == ERROR_SUCCESS) {
          len = sizeof(enableKFW);
          code = RegQueryValueEx(parmKey, "EnableKFW", NULL, NULL,
                                  (BYTE *) &enableKFW, &len);
          if (code != ERROR_SUCCESS) {
!             RegCloseKey(parmKey);
! 
!             code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, OpenAFSConfigKeyName,
!                                  0, KEY_QUERY_VALUE, &parmKey);
!             if (code == ERROR_SUCCESS) {
!                 len = sizeof(enableKFW);
!                 code = RegQueryValueEx(parmKey, "EnableKFW", NULL, NULL,
!                                         (BYTE *) &enableKFW, &len);
!                 if (code != ERROR_SUCCESS)
!                     enableKFW = 1;
!             }
          }
          RegCloseKey (parmKey);
!     }
      if ( !enableKFW )
          return FALSE;
  
***************
*** 890,900 ****
  
      princ_realm = krb5_princ_realm(ctx, princ);
      for ( i=0; i<princ_realm->length; i++ ) {
! 		realm[i] = princ_realm->data[i];
          cell[i] = tolower(princ_realm->data[i]);
      }
! 	cell[i] = '\0';
! 	realm[i] = '\0';
  
      code = KFW_AFS_klog(ctx, cc, "afs", cell, realm, pLeash_get_default_lifetime(),NULL);
      if ( IsDebuggerPresent() ) {
--- 927,937 ----
  
      princ_realm = krb5_princ_realm(ctx, princ);
      for ( i=0; i<princ_realm->length; i++ ) {
!         realm[i] = princ_realm->data[i];
          cell[i] = tolower(princ_realm->data[i]);
      }
!     cell[i] = '\0';
!     realm[i] = '\0';
  
      code = KFW_AFS_klog(ctx, cc, "afs", cell, realm, pLeash_get_default_lifetime(),NULL);
      if ( IsDebuggerPresent() ) {
***************
*** 2546,2555 ****
      memset(ServiceName, '\0', sizeof(ServiceName));
      memset(realm_of_user, '\0', sizeof(realm_of_user));
      memset(realm_of_cell, '\0', sizeof(realm_of_cell));
! 	if (cell && cell[0])
! 		strcpy(Dmycell, cell);
! 	else
! 		memset(Dmycell, '\0', sizeof(Dmycell));
  
      // NULL or empty cell returns information on local cell
      if (rc = KFW_AFS_get_cellconfig(Dmycell, &ak_cellconfig, local_cell))
--- 2583,2592 ----
      memset(ServiceName, '\0', sizeof(ServiceName));
      memset(realm_of_user, '\0', sizeof(realm_of_user));
      memset(realm_of_cell, '\0', sizeof(realm_of_cell));
!     if (cell && cell[0])
!         strcpy(Dmycell, cell);
!     else
!         memset(Dmycell, '\0', sizeof(Dmycell));
  
      // NULL or empty cell returns information on local cell
      if (rc = KFW_AFS_get_cellconfig(Dmycell, &ak_cellconfig, local_cell))
***************
*** 2575,2587 ****
      memset((char *)&increds, 0, sizeof(increds));
  
      code = pkrb5_cc_get_principal(ctx, cc, &client_principal);
! 	if (code) {
          if ( code == KRB5_CC_NOTFOUND && IsDebuggerPresent() ) 
          {
              OutputDebugString("Principal Not Found for ccache\n");
          }
          goto skip_krb5_init;
      }
      i = krb5_princ_realm(ctx, client_principal)->length;
      if (i > REALM_SZ-1) 
          i = REALM_SZ-1;
--- 2612,2632 ----
      memset((char *)&increds, 0, sizeof(increds));
  
      code = pkrb5_cc_get_principal(ctx, cc, &client_principal);
!     if (code) {
          if ( code == KRB5_CC_NOTFOUND && IsDebuggerPresent() ) 
          {
              OutputDebugString("Principal Not Found for ccache\n");
          }
          goto skip_krb5_init;
      }
+ 
+     if ( strchr(krb5_princ_component(ctx,client_principal,0),'.') != NULL )
+     {
+         OutputDebugString("Illegal Principal name contains dot in first component\n");
+         rc = KRB5KRB_ERR_GENERIC;
+         goto cleanup;
+     }
+ 
      i = krb5_princ_realm(ctx, client_principal)->length;
      if (i > REALM_SZ-1) 
          i = REALM_SZ-1;
***************
*** 2761,2767 ****
           * No need to perform a krb524 translation which is 
           * commented out in the code below
           */
!         if (k5creds->ticket.length > MAXKTCTICKETLEN)
              goto try_krb524d;
  
          memset(&aserver, '\0', sizeof(aserver));
--- 2806,2813 ----
           * No need to perform a krb524 translation which is 
           * commented out in the code below
           */
!         if (KFW_use_krb524() ||
!             k5creds->ticket.length > MAXKTCTICKETLEN)
              goto try_krb524d;
  
          memset(&aserver, '\0', sizeof(aserver));
Index: openafs/src/WINNT/afsd/afsshare.c
diff -c openafs/src/WINNT/afsd/afsshare.c:1.5 openafs/src/WINNT/afsd/afsshare.c:1.5.2.1
*** openafs/src/WINNT/afsd/afsshare.c:1.5	Tue Jul 20 10:36:41 2004
--- openafs/src/WINNT/afsd/afsshare.c	Wed Sep  8 01:58:33 2004
***************
*** 65,71 ****
              else
                  mountstring = argv[2];
  
!             if (RegSetValueEx(hkSubmounts, argv[1], 0, REG_SZ, mountstring, strlen(mountstring)+1)) {
                  fprintf(stderr,"Submount Set failure for [%s]: %lX",
                           argv[1], GetLastError());
                  RegCloseKey(hkSubmounts);
--- 65,71 ----
              else
                  mountstring = argv[2];
  
!             if (RegSetValueEx(hkSubmounts, argv[1], 0, REG_EXPAND_SZ, mountstring, strlen(mountstring)+1)) {
                  fprintf(stderr,"Submount Set failure for [%s]: %lX",
                           argv[1], GetLastError());
                  RegCloseKey(hkSubmounts);
Index: openafs/src/WINNT/afsd/cm_buf.c
diff -c openafs/src/WINNT/afsd/cm_buf.c:1.13.2.1 openafs/src/WINNT/afsd/cm_buf.c:1.13.2.2
*** openafs/src/WINNT/afsd/cm_buf.c:1.13.2.1	Tue Aug 17 00:28:38 2004
--- openafs/src/WINNT/afsd/cm_buf.c	Sun Oct 10 19:52:04 2004
***************
*** 110,167 ****
  /* hold a reference to an already held buffer */
  void buf_Hold(cm_buf_t *bp)
  {
! 	lock_ObtainWrite(&buf_globalLock);
! 	bp->refCount++;
! 	lock_ReleaseWrite(&buf_globalLock);
  }
  
  /* incremental sync daemon.  Writes 1/10th of all the buffers every 5000 ms */
  void buf_IncrSyncer(long parm)
  {
! 	cm_buf_t *bp;			/* buffer we're hacking on; held */
!         long i;				/* counter */
!         long nAtOnce;			/* how many to do at once */
! 	cm_req_t req;
! 
! 	lock_ObtainWrite(&buf_globalLock);
! 	bp = buf_allp;
!         bp->refCount++;
!         lock_ReleaseWrite(&buf_globalLock);
!         nAtOnce = buf_nbuffers / 10;
! 	while (1) {
  #ifndef DJGPP
!                 i = SleepEx(5000, 1);
!                 if (i != 0) continue;
  #else
! 		thrd_Sleep(5000);
  #endif /* DJGPP */
                  
!                 /* now go through our percentage of the buffers */
!                 for(i=0; i<nAtOnce; i++) {
! 			/* don't want its identity changing while we're
!                          * messing with it, so must do all of this with
!                          * bp held.
! 			 */
! 
! 			/* start cleaning the buffer; don't touch log pages since
!                          * the log code counts on knowing exactly who is writing
!                          * a log page at any given instant.
!                          */
! 			cm_InitReq(&req);
! 			req.flags |= CM_REQ_NORETRY;
! 			buf_CleanAsync(bp, &req);
! 
! 			/* now advance to the next buffer; the allp chain never changes,
!                          * and so can be followed even when holding no locks.
!                          */
! 			lock_ObtainWrite(&buf_globalLock);
! 			buf_LockedRelease(bp);
!                         bp = bp->allp;
!                         if (!bp) bp = buf_allp;
!                         bp->refCount++;
! 			lock_ReleaseWrite(&buf_globalLock);
!                 }	/* for loop over a bunch of buffers */
!         }		/* whole daemon's while loop */
  }
  
  #ifndef DJGPP
--- 110,167 ----
  /* hold a reference to an already held buffer */
  void buf_Hold(cm_buf_t *bp)
  {
!     lock_ObtainWrite(&buf_globalLock);
!     bp->refCount++;
!     lock_ReleaseWrite(&buf_globalLock);
  }
  
  /* incremental sync daemon.  Writes 1/10th of all the buffers every 5000 ms */
  void buf_IncrSyncer(long parm)
  {
!     cm_buf_t *bp;			/* buffer we're hacking on; held */
!     long i;				/* counter */
!     long nAtOnce;			/* how many to do at once */
!     cm_req_t req;
! 
!     lock_ObtainWrite(&buf_globalLock);
!     bp = buf_allp;
!     bp->refCount++;
!     lock_ReleaseWrite(&buf_globalLock);
!     nAtOnce = buf_nbuffers / 10;
!     while (1) {
  #ifndef DJGPP
!         i = SleepEx(5000, 1);
!         if (i != 0) continue;
  #else
!         thrd_Sleep(5000);
  #endif /* DJGPP */
                  
!         /* now go through our percentage of the buffers */
!         for(i=0; i<nAtOnce; i++) {
!             /* don't want its identity changing while we're
!              * messing with it, so must do all of this with
!              * bp held.
!              */
! 
!             /* start cleaning the buffer; don't touch log pages since
!              * the log code counts on knowing exactly who is writing
!              * a log page at any given instant.
!              */
!             cm_InitReq(&req);
!             req.flags |= CM_REQ_NORETRY;
!             buf_CleanAsync(bp, &req);
! 
!             /* now advance to the next buffer; the allp chain never changes,
!              * and so can be followed even when holding no locks.
!              */
!             lock_ObtainWrite(&buf_globalLock);
!             buf_LockedRelease(bp);
!             bp = bp->allp;
!             if (!bp) bp = buf_allp;
!             bp->refCount++;
!             lock_ReleaseWrite(&buf_globalLock);
!         }	/* for loop over a bunch of buffers */
!     }		/* whole daemon's while loop */
  }
  
  #ifndef DJGPP
***************
*** 172,229 ****
   */
  PSECURITY_ATTRIBUTES CreateCacheFileSA()
  {
! 	PSECURITY_ATTRIBUTES psa;
! 	PSECURITY_DESCRIPTOR psd;
! 	SID_IDENTIFIER_AUTHORITY authority = SECURITY_NT_AUTHORITY;
! 	PSID AdminSID;
! 	DWORD AdminSIDlength;
! 	PACL AdminOnlyACL;
! 	DWORD ACLlength;
! 
! 	/* Get Administrator SID */
! 	AllocateAndInitializeSid(&authority, 2,
! 				 SECURITY_BUILTIN_DOMAIN_RID,
! 				 DOMAIN_ALIAS_RID_ADMINS,
! 				 0, 0, 0, 0, 0, 0,
! 				 &AdminSID);
! 
! 	/* Create Administrator-only ACL */
! 	AdminSIDlength = GetLengthSid(AdminSID);
! 	ACLlength = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE)
! 			+ AdminSIDlength - sizeof(DWORD);
! 	AdminOnlyACL = GlobalAlloc(GMEM_FIXED, ACLlength);
! 	InitializeAcl(AdminOnlyACL, ACLlength, ACL_REVISION);
! 	AddAccessAllowedAce(AdminOnlyACL, ACL_REVISION,
! 			    STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,
! 			    AdminSID);
! 
! 	/* Create security descriptor */
! 	psd = GlobalAlloc(GMEM_FIXED, sizeof(SECURITY_DESCRIPTOR));
! 	InitializeSecurityDescriptor(psd, SECURITY_DESCRIPTOR_REVISION);
! 	SetSecurityDescriptorDacl(psd, TRUE, AdminOnlyACL, FALSE);
! 
! 	/* Create security attributes structure */
! 	psa = GlobalAlloc(GMEM_FIXED, sizeof(SECURITY_ATTRIBUTES));
! 	psa->nLength = sizeof(SECURITY_ATTRIBUTES);
! 	psa->lpSecurityDescriptor = psd;
! 	psa->bInheritHandle = TRUE;
  
! 	return psa;
! }
  #endif /* !DJGPP */
  
  #ifndef DJGPP
  /* Free a security attribute structure created by CreateCacheFileSA() */
  VOID FreeCacheFileSA(PSECURITY_ATTRIBUTES psa)
  {
! 	BOOL b1, b2;
! 	PACL pAcl;
  
! 	GetSecurityDescriptorDacl(psa->lpSecurityDescriptor, &b1, &pAcl, &b2);
! 	GlobalFree(pAcl);
! 	GlobalFree(psa->lpSecurityDescriptor);
! 	GlobalFree(psa);
! }
  #endif /* !DJGPP */
  	
  /* initialize the buffer package; called with no locks
--- 172,229 ----
   */
  PSECURITY_ATTRIBUTES CreateCacheFileSA()
  {
!     PSECURITY_ATTRIBUTES psa;
!     PSECURITY_DESCRIPTOR psd;
!     SID_IDENTIFIER_AUTHORITY authority = SECURITY_NT_AUTHORITY;
!     PSID AdminSID;
!     DWORD AdminSIDlength;
!     PACL AdminOnlyACL;
!     DWORD ACLlength;
! 
!     /* Get Administrator SID */
!     AllocateAndInitializeSid(&authority, 2,
!                               SECURITY_BUILTIN_DOMAIN_RID,
!                               DOMAIN_ALIAS_RID_ADMINS,
!                               0, 0, 0, 0, 0, 0,
!                               &AdminSID);
! 
!     /* Create Administrator-only ACL */
!     AdminSIDlength = GetLengthSid(AdminSID);
!     ACLlength = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE)
!         + AdminSIDlength - sizeof(DWORD);
!     AdminOnlyACL = GlobalAlloc(GMEM_FIXED, ACLlength);
!     InitializeAcl(AdminOnlyACL, ACLlength, ACL_REVISION);
!     AddAccessAllowedAce(AdminOnlyACL, ACL_REVISION,
!                          STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,
!                          AdminSID);
! 
!     /* Create security descriptor */
!     psd = GlobalAlloc(GMEM_FIXED, sizeof(SECURITY_DESCRIPTOR));
!     InitializeSecurityDescriptor(psd, SECURITY_DESCRIPTOR_REVISION);
!     SetSecurityDescriptorDacl(psd, TRUE, AdminOnlyACL, FALSE);
! 
!     /* Create security attributes structure */
!     psa = GlobalAlloc(GMEM_FIXED, sizeof(SECURITY_ATTRIBUTES));
!     psa->nLength = sizeof(SECURITY_ATTRIBUTES);
!     psa->lpSecurityDescriptor = psd;
!     psa->bInheritHandle = TRUE;
  
!     return psa;
! }       
  #endif /* !DJGPP */
  
  #ifndef DJGPP
  /* Free a security attribute structure created by CreateCacheFileSA() */
  VOID FreeCacheFileSA(PSECURITY_ATTRIBUTES psa)
  {
!     BOOL b1, b2;
!     PACL pAcl;
  
!     GetSecurityDescriptorDacl(psa->lpSecurityDescriptor, &b1, &pAcl, &b2);
!     GlobalFree(pAcl);
!     GlobalFree(psa->lpSecurityDescriptor);
!     GlobalFree(psa);
! }       
  #endif /* !DJGPP */
  	
  /* initialize the buffer package; called with no locks
***************
*** 231,397 ****
   */
  long buf_Init(cm_buf_ops_t *opsp)
  {
! 	static osi_once_t once;
!         cm_buf_t *bp;
!         long sectorSize;
!         thread_t phandle;
  #ifndef DJGPP
! 	HANDLE hf, hm;
! 	PSECURITY_ATTRIBUTES psa;
  #endif /* !DJGPP */
! 	long i;
!         unsigned long pid;
! 	char *data;
! 	long cs;
  
  #ifndef DJGPP
! 	/* Get system info; all we really want is the allocation granularity */ 
! 	GetSystemInfo(&sysInfo);
  #endif /* !DJGPP */
  
! 	/* Have to be able to reserve a whole chunk */
! 	if (((buf_nbuffers - 3) * buf_bufferSize) < cm_chunkSize)
! 		return CM_ERROR_TOOFEWBUFS;
! 
! 	/* recall for callouts */
! 	cm_buf_opsp = opsp;
! 
!         if (osi_Once(&once)) {
! 		/* initialize global locks */
! 		lock_InitializeRWLock(&buf_globalLock, "Global buffer lock");
  
  #ifndef DJGPP
! 		/*
! 		 * Cache file mapping constrained by
! 		 * system allocation granularity;
! 		 * round up, assuming granularity is a power of two
! 		 */
! 		cs = buf_nbuffers * buf_bufferSize;
! 		cs = (cs + (sysInfo.dwAllocationGranularity - 1))
! 			& ~(sysInfo.dwAllocationGranularity - 1);
! 		if (cs != buf_nbuffers * buf_bufferSize) {
! 			buf_nbuffers = cs / buf_bufferSize;
! 			afsi_log("Cache size rounded up to %d buffers",
! 				 buf_nbuffers);
! 		}
  #endif /* !DJGPP */
  
! 		/* remember this for those who want to reset it */
! 	        buf_nOrigBuffers = buf_nbuffers;
  
! 		/* lower hash size to a prime number */
!                 buf_hashSize = osi_PrimeLessThan(buf_hashSize);
  
! 		/* create hash table */
!                 buf_hashTablepp = malloc(buf_hashSize * sizeof(cm_buf_t *));
!                 memset((void *)buf_hashTablepp, 0,
! 			buf_hashSize * sizeof(cm_buf_t *));
! 
! 		/* another hash table */
!                 buf_fileHashTablepp = malloc(buf_hashSize * sizeof(cm_buf_t *));
!                 memset((void *)buf_fileHashTablepp, 0,
! 			buf_hashSize * sizeof(cm_buf_t *));
                  
! 		/* min value for which this works */
! 		sectorSize = 1;
  
  #ifndef DJGPP
!         if(buf_cacheType == CM_BUF_CACHETYPE_FILE) {
! 		/* Reserve buffer space by mapping cache file */
! 		psa = CreateCacheFileSA();
! 		hf = CreateFile(cm_CachePath,
! 			GENERIC_READ | GENERIC_WRITE,
! 			FILE_SHARE_READ | FILE_SHARE_WRITE,
! 			psa,
! 			OPEN_ALWAYS,
! 			FILE_ATTRIBUTE_NORMAL,
! 			NULL);
! 		if (hf == INVALID_HANDLE_VALUE) {
! 			afsi_log("create file error %d", GetLastError());
! 			return CM_ERROR_INVAL;
! 		}
! 		FreeCacheFileSA(psa);
          } else { /* buf_cacheType == CM_BUF_CACHETYPE_VIRTUAL */
              hf = INVALID_HANDLE_VALUE;
          }
! 		CacheHandle = hf;
! 		hm = CreateFileMapping(hf,
! 			NULL,
! 			PAGE_READWRITE,
! 			0, buf_nbuffers * buf_bufferSize,
! 			NULL);
! 		if (hm == NULL) {
! 			if (GetLastError() == ERROR_DISK_FULL) {
! 				afsi_log("Error creating cache file mapping: disk full");
! 				return CM_ERROR_TOOMANYBUFS;
! 			}
! 			return CM_ERROR_INVAL;
! 		}
! 		data = MapViewOfFile(hm,
! 			FILE_MAP_ALL_ACCESS,
! 			0, 0,
! 			buf_nbuffers * buf_bufferSize);
! 		if (data == NULL) {
! 			if(hf != INVALID_HANDLE_VALUE) CloseHandle(hf);
! 			CloseHandle(hm);
! 			return CM_ERROR_INVAL;
! 		}
! 		CloseHandle(hm);
! #else
!                 /* djgpp doesn't support memory mapped files */
!                 data = malloc(buf_nbuffers * buf_bufferSize);
  #endif /* !DJGPP */
  
!                 /* create buffer headers and put in free list */
! 		bp = malloc(buf_nbuffers * sizeof(cm_buf_t));
!                 buf_allp = NULL;
!                 for(i=0; i<buf_nbuffers; i++) {
! 			/* allocate and zero some storage */
!                         memset(bp, 0, sizeof(cm_buf_t));
! 
! 			/* thread on list of all buffers */
!                         bp->allp = buf_allp;
!                         buf_allp = bp;
!                         
!                         osi_QAdd((osi_queue_t **)&buf_freeListp, &bp->q);
!                         bp->flags |= CM_BUF_INLRU;
!                         lock_InitializeMutex(&bp->mx, "Buffer mutex");
! 
! 			/* grab appropriate number of bytes from aligned zone */
!                         bp->datap = data;
! 
! 			/* setup last buffer pointer */
! 	                if (i == 0)
!                         	buf_freeListEndp = bp;
! 
! 			/* next */
! 			bp++;
! 			data += buf_bufferSize;
!                 }
!                 
! 		/* none reserved at first */
!                 buf_reservedBufs = 0;
!                 
!                 /* just for safety's sake */
!                 buf_maxReservedBufs = buf_nbuffers - 3;
!                 
!                 /* init the buffer trace log */
!                 buf_logp = osi_LogCreate("buffer", 10);
  
! 		osi_EndOnce(&once);
!                 
!                 /* and create the incr-syncer */
!                 phandle = thrd_Create(0, 0,
!                                       (ThreadFunc) buf_IncrSyncer, 0, 0, &pid,
!                                       "buf_IncrSyncer");
  
! 		osi_assertx(phandle != NULL, "buf: can't create incremental sync proc");
  #ifndef DJGPP
! 		CloseHandle(phandle);
  #endif /* !DJGPP */
!         }
  
! 	return 0;
  }
  
  /* add nbuffers to the buffer pool, if possible.
--- 231,400 ----
   */
  long buf_Init(cm_buf_ops_t *opsp)
  {
!     static osi_once_t once;
!     cm_buf_t *bp;
!     long sectorSize;
!     thread_t phandle;
  #ifndef DJGPP
!     HANDLE hf, hm;
!     PSECURITY_ATTRIBUTES psa;
  #endif /* !DJGPP */
!     long i;
!     unsigned long pid;
!     char *data;
!     long cs;
  
  #ifndef DJGPP
!     /* Get system info; all we really want is the allocation granularity */ 
!     GetSystemInfo(&sysInfo);
  #endif /* !DJGPP */
  
!     /* Have to be able to reserve a whole chunk */
!     if (((buf_nbuffers - 3) * buf_bufferSize) < cm_chunkSize)
!         return CM_ERROR_TOOFEWBUFS;
! 
!     /* recall for callouts */
!     cm_buf_opsp = opsp;
! 
!     if (osi_Once(&once)) {
!         /* initialize global locks */
!         lock_InitializeRWLock(&buf_globalLock, "Global buffer lock");
  
  #ifndef DJGPP
!         /*
!         * Cache file mapping constrained by
!          * system allocation granularity;
!          * round up, assuming granularity is a power of two
!          */
!         cs = buf_nbuffers * buf_bufferSize;
!         cs = (cs + (sysInfo.dwAllocationGranularity - 1))
!             & ~(sysInfo.dwAllocationGranularity - 1);
!         if (cs != buf_nbuffers * buf_bufferSize) {
!             buf_nbuffers = cs / buf_bufferSize;
!             afsi_log("Cache size rounded up to %d buffers",
!                       buf_nbuffers);
!         }
  #endif /* !DJGPP */
  
!         /* remember this for those who want to reset it */
!         buf_nOrigBuffers = buf_nbuffers;
  
!         /* lower hash size to a prime number */
!         buf_hashSize = osi_PrimeLessThan(buf_hashSize);
  
!         /* create hash table */
!         buf_hashTablepp = malloc(buf_hashSize * sizeof(cm_buf_t *));
!         memset((void *)buf_hashTablepp, 0,
!                 buf_hashSize * sizeof(cm_buf_t *));
! 
!         /* another hash table */
!         buf_fileHashTablepp = malloc(buf_hashSize * sizeof(cm_buf_t *));
!         memset((void *)buf_fileHashTablepp, 0,
!                 buf_hashSize * sizeof(cm_buf_t *));
                  
!         /* min value for which this works */
!         sectorSize = 1;
  
  #ifndef DJGPP
!         if (buf_cacheType == CM_BUF_CACHETYPE_FILE) {
!             /* Reserve buffer space by mapping cache file */
!             psa = CreateCacheFileSA();
!             hf = CreateFile(cm_CachePath,
!                              GENERIC_READ | GENERIC_WRITE,
!                              FILE_SHARE_READ | FILE_SHARE_WRITE,
!                              psa,
!                              OPEN_ALWAYS,
!                              FILE_ATTRIBUTE_NORMAL,
!                              NULL);
!             if (hf == INVALID_HANDLE_VALUE) {
!                 afsi_log("Error creating cache file \"%s\" error %d", 
!                           cm_CachePath, GetLastError());
!                 return CM_ERROR_INVAL;
!             }
!             FreeCacheFileSA(psa);
          } else { /* buf_cacheType == CM_BUF_CACHETYPE_VIRTUAL */
              hf = INVALID_HANDLE_VALUE;
          }
!         CacheHandle = hf;
!         hm = CreateFileMapping(hf,
!                                 NULL,
!                                 PAGE_READWRITE,
!                                 0, buf_nbuffers * buf_bufferSize,
!                                 NULL);
!         if (hm == NULL) {
!             if (GetLastError() == ERROR_DISK_FULL) {
!                 afsi_log("Error creating cache file \"%s\" mapping: disk full",
!                           cm_CachePath);
!                 return CM_ERROR_TOOMANYBUFS;
!             }
!             return CM_ERROR_INVAL;
!         }
!         data = MapViewOfFile(hm,
!                               FILE_MAP_ALL_ACCESS,
!                               0, 0,   
!                               buf_nbuffers * buf_bufferSize);
!         if (data == NULL) {
!             if (hf != INVALID_HANDLE_VALUE)
!                 CloseHandle(hf);
!             CloseHandle(hm);
!             return CM_ERROR_INVAL;
!         }
!         CloseHandle(hm);
! #else   
!         /* djgpp doesn't support memory mapped files */
!         data = malloc(buf_nbuffers * buf_bufferSize);
  #endif /* !DJGPP */
  
!         /* create buffer headers and put in free list */
!         bp = malloc(buf_nbuffers * sizeof(cm_buf_t));
!         buf_allp = NULL;
!         for(i=0; i<buf_nbuffers; i++) {
!             /* allocate and zero some storage */
!             memset(bp, 0, sizeof(cm_buf_t));
! 
!             /* thread on list of all buffers */
!             bp->allp = buf_allp;
!             buf_allp = bp;
! 
!             osi_QAdd((osi_queue_t **)&buf_freeListp, &bp->q);
!             bp->flags |= CM_BUF_INLRU;
!             lock_InitializeMutex(&bp->mx, "Buffer mutex");
! 
!             /* grab appropriate number of bytes from aligned zone */
!             bp->datap = data;
! 
!             /* setup last buffer pointer */
!             if (i == 0)
!                 buf_freeListEndp = bp;
! 
!             /* next */
!             bp++;
!             data += buf_bufferSize;
!         }
  
!         /* none reserved at first */
!         buf_reservedBufs = 0;
! 
!         /* just for safety's sake */
!         buf_maxReservedBufs = buf_nbuffers - 3;
! 
!         /* init the buffer trace log */
!         buf_logp = osi_LogCreate("buffer", 10);
! 
!         osi_EndOnce(&once);
! 
!         /* and create the incr-syncer */
!         phandle = thrd_Create(0, 0,
!                                (ThreadFunc) buf_IncrSyncer, 0, 0, &pid,
!                                "buf_IncrSyncer");
  
!         osi_assertx(phandle != NULL, "buf: can't create incremental sync proc");
  #ifndef DJGPP
!         CloseHandle(phandle);
  #endif /* !DJGPP */
!     }
  
!     return 0;
  }
  
  /* add nbuffers to the buffer pool, if possible.
***************
*** 399,410 ****
   */
  long buf_AddBuffers(long nbuffers)
  {
! 	cm_buf_t *bp;
!         int i;
! 	char *data;
  #ifndef DJGPP
! 	HANDLE hm;
! 	long cs;
  
      afsi_log("%d buffers being added to the existing cache of size %d",
                nbuffers, buf_nbuffers);
--- 402,413 ----
   */
  long buf_AddBuffers(long nbuffers)
  {
!     cm_buf_t *bp;
!     int i;
!     char *data;
  #ifndef DJGPP
!     HANDLE hm;
!     long cs;
  
      afsi_log("%d buffers being added to the existing cache of size %d",
                nbuffers, buf_nbuffers);
***************
*** 418,490 ****
          return CM_ERROR_INVAL;
      }
  
! 	/*
! 	 * Cache file mapping constrained by
! 	 * system allocation granularity;
! 	 * round up, assuming granularity is a power of two;
! 	 * assume existing cache size is already rounded
! 	 */
! 	cs = nbuffers * buf_bufferSize;
! 	cs = (cs + (sysInfo.dwAllocationGranularity - 1))
! 		& ~(sysInfo.dwAllocationGranularity - 1);
! 	if (cs != nbuffers * buf_bufferSize) {
! 		nbuffers = cs / buf_bufferSize;
! 	}
! 
! 	/* Reserve additional buffer space by remapping cache file */
! 	hm = CreateFileMapping(CacheHandle,
! 		NULL,
! 		PAGE_READWRITE,
! 		0, (buf_nbuffers + nbuffers) * buf_bufferSize,
! 		NULL);
! 	if (hm == NULL) {
! 		if (GetLastError() == ERROR_DISK_FULL)
! 			return CM_ERROR_TOOMANYBUFS;
! 		else
! 			return CM_ERROR_INVAL;
! 	}
! 	data = MapViewOfFile(hm,
! 		FILE_MAP_ALL_ACCESS,
! 		0, buf_nbuffers * buf_bufferSize,
! 		nbuffers * buf_bufferSize);
! 	if (data == NULL) {
! 		CloseHandle(hm);
! 		return CM_ERROR_INVAL;
! 	}
! 	CloseHandle(hm);
  #else
!         data = malloc(buf_nbuffers * buf_bufferSize);
  #endif /* DJGPP */
  
! 	/* Create buffer headers and put in free list */
!         bp = malloc(nbuffers * sizeof(*bp));
  
! 	for(i=0; i<nbuffers; i++) {
! 	        memset(bp, 0, sizeof(*bp));
          
! 	        lock_InitializeMutex(&bp->mx, "cm_buf_t");
  
! 		/* grab appropriate number of bytes from aligned zone */
! 		bp->datap = data;
  
!                 bp->flags |= CM_BUF_INLRU;
!                 
!                 lock_ObtainWrite(&buf_globalLock);
! 		/* note that buf_allp chain is covered by buf_globalLock now */
!                 bp->allp = buf_allp;
!                 buf_allp = bp;
!                 osi_QAdd((osi_queue_t **) &buf_freeListp, &bp->q);
!                 if (!buf_freeListEndp) buf_freeListEndp = bp;
!                 buf_nbuffers++;
!                 lock_ReleaseWrite(&buf_globalLock);
  
! 		bp++;
! 		data += buf_bufferSize;
  	
!         }	 /* for loop over all buffers */
  
!         return 0;
! }
  
  /* interface to set the number of buffers to an exact figure.
   * Called with no locks held.
--- 421,493 ----
          return CM_ERROR_INVAL;
      }
  
!     /*
!      * Cache file mapping constrained by
!      * system allocation granularity;
!      * round up, assuming granularity is a power of two;
!      * assume existing cache size is already rounded
!      */
!     cs = nbuffers * buf_bufferSize;
!     cs = (cs + (sysInfo.dwAllocationGranularity - 1))
!         & ~(sysInfo.dwAllocationGranularity - 1);
!     if (cs != nbuffers * buf_bufferSize) {
!         nbuffers = cs / buf_bufferSize;
!     }
! 
!     /* Reserve additional buffer space by remapping cache file */
!     hm = CreateFileMapping(CacheHandle,
!                             NULL,
!                             PAGE_READWRITE,
!                             0, (buf_nbuffers + nbuffers) * buf_bufferSize,
!                             NULL);
!     if (hm == NULL) {
!         if (GetLastError() == ERROR_DISK_FULL)
!             return CM_ERROR_TOOMANYBUFS;
!         else
!             return CM_ERROR_INVAL;
!     }
!     data = MapViewOfFile(hm,
!                           FILE_MAP_ALL_ACCESS,
!                           0, buf_nbuffers * buf_bufferSize,
!                           nbuffers * buf_bufferSize);
!     if (data == NULL) {
!         CloseHandle(hm);
!         return CM_ERROR_INVAL;
!     }
!     CloseHandle(hm);
  #else
!     data = malloc(buf_nbuffers * buf_bufferSize);
  #endif /* DJGPP */
  
!     /* Create buffer headers and put in free list */
!     bp = malloc(nbuffers * sizeof(*bp));
  
!     for(i=0; i<nbuffers; i++) {
!         memset(bp, 0, sizeof(*bp));
          
!         lock_InitializeMutex(&bp->mx, "cm_buf_t");
  
!         /* grab appropriate number of bytes from aligned zone */
!         bp->datap = data;
  
!         bp->flags |= CM_BUF_INLRU;
! 
!         lock_ObtainWrite(&buf_globalLock);
!         /* note that buf_allp chain is covered by buf_globalLock now */
!         bp->allp = buf_allp;
!         buf_allp = bp;
!         osi_QAdd((osi_queue_t **) &buf_freeListp, &bp->q);
!         if (!buf_freeListEndp) buf_freeListEndp = bp;
!         buf_nbuffers++;
!         lock_ReleaseWrite(&buf_globalLock);
  
!         bp++;
!         data += buf_bufferSize;
  	
!     }	 /* for loop over all buffers */
  
!     return 0;
! }       
  
  /* interface to set the number of buffers to an exact figure.
   * Called with no locks held.
***************
*** 495,502 ****
          return CM_ERROR_INVAL;
      if (nbuffers == buf_nbuffers) 
          return 0;
!         else if (nbuffers > buf_nbuffers)
! 		return buf_AddBuffers(nbuffers - buf_nbuffers);
      else 
          return CM_ERROR_INVAL;
  }
--- 498,505 ----
          return CM_ERROR_INVAL;
      if (nbuffers == buf_nbuffers) 
          return 0;
!     else if (nbuffers > buf_nbuffers)
!         return buf_AddBuffers(nbuffers - buf_nbuffers);
      else 
          return CM_ERROR_INVAL;
  }
***************
*** 504,512 ****
  /* release a buffer.  Buffer must be referenced, but unlocked. */
  void buf_Release(cm_buf_t *bp)
  {
! 	lock_ObtainWrite(&buf_globalLock);
! 	buf_LockedRelease(bp);
! 	lock_ReleaseWrite(&buf_globalLock);
  }
  
  /* wait for reading or writing to clear; called with write-locked
--- 507,515 ----
  /* release a buffer.  Buffer must be referenced, but unlocked. */
  void buf_Release(cm_buf_t *bp)
  {
!     lock_ObtainWrite(&buf_globalLock);
!     buf_LockedRelease(bp);
!     lock_ReleaseWrite(&buf_globalLock);
  }
  
  /* wait for reading or writing to clear; called with write-locked
***************
*** 514,523 ****
   */
  void buf_WaitIO(cm_buf_t *bp)
  {
! 	while (1) {
! 		/* if no IO is happening, we're done */
! 		if (!(bp->flags & (CM_BUF_READING | CM_BUF_WRITING)))
! 			break;
  		
          /* otherwise I/O is happening, but some other thread is waiting for
           * the I/O already.  Wait for that guy to figure out what happened,
--- 517,526 ----
   */
  void buf_WaitIO(cm_buf_t *bp)
  {
!     while (1) {
!         /* if no IO is happening, we're done */
!         if (!(bp->flags & (CM_BUF_READING | CM_BUF_WRITING)))
!             break;
  		
          /* otherwise I/O is happening, but some other thread is waiting for
           * the I/O already.  Wait for that guy to figure out what happened,
***************
*** 536,542 ****
       * the I/O to complete.  Do so.
       */
      if (bp->flags & CM_BUF_WAITING) {
! 		bp->flags &= ~CM_BUF_WAITING;
          osi_Wakeup((long) bp);
      }
      osi_Log1(buf_logp, "WaitIO finished wait for bp 0x%x", (long) bp);
--- 539,545 ----
       * the I/O to complete.  Do so.
       */
      if (bp->flags & CM_BUF_WAITING) {
!         bp->flags &= ~CM_BUF_WAITING;
          osi_Wakeup((long) bp);
      }
      osi_Log1(buf_logp, "WaitIO finished wait for bp 0x%x", (long) bp);
***************
*** 545,584 ****
  /* code to drop reference count while holding buf_globalLock */
  void buf_LockedRelease(cm_buf_t *bp)
  {
! 	/* ensure that we're in the LRU queue if our ref count is 0 */
! 	osi_assert(bp->refCount > 0);
! 	if (--bp->refCount == 0) {
! 		if (!(bp->flags & CM_BUF_INLRU)) {
! 			osi_QAdd((osi_queue_t **) &buf_freeListp, &bp->q);
! 
!       			/* watch for transition from empty to one element */
!                         if (!buf_freeListEndp)
!                         	buf_freeListEndp = buf_freeListp;
! 			bp->flags |= CM_BUF_INLRU;
!                 }
          }
! }
  
  /* find a buffer, if any, for a particular file ID and offset.  Assumes
   * that buf_globalLock is write locked when called.
   */
  cm_buf_t *buf_LockedFind(struct cm_scache *scp, osi_hyper_t *offsetp)
  {
! 	long i;
!         cm_buf_t *bp;
!         
!         i = BUF_HASH(&scp->fid, offsetp);
!         for(bp = buf_hashTablepp[i]; bp; bp=bp->hashp) {
! 		if (cm_FidCmp(&scp->fid, &bp->fid) == 0
! 			&& offsetp->LowPart == bp->offset.LowPart
!                 	&& offsetp->HighPart == bp->offset.HighPart) {
! 			bp->refCount++;
! 			break;
!                 }
          }
          
! 	/* return whatever we found, if anything */
!         return bp;
  }
  
  /* find a buffer with offset *offsetp for vnode *scp.  Called
--- 548,587 ----
  /* code to drop reference count while holding buf_globalLock */
  void buf_LockedRelease(cm_buf_t *bp)
  {
!     /* ensure that we're in the LRU queue if our ref count is 0 */
!     osi_assert(bp->refCount > 0);
!     if (--bp->refCount == 0) {
!         if (!(bp->flags & CM_BUF_INLRU)) {
!             osi_QAdd((osi_queue_t **) &buf_freeListp, &bp->q);
! 
!             /* watch for transition from empty to one element */
!             if (!buf_freeListEndp)
!                 buf_freeListEndp = buf_freeListp;
!             bp->flags |= CM_BUF_INLRU;
          }
!     }
! }       
  
  /* find a buffer, if any, for a particular file ID and offset.  Assumes
   * that buf_globalLock is write locked when called.
   */
  cm_buf_t *buf_LockedFind(struct cm_scache *scp, osi_hyper_t *offsetp)
  {
!     long i;
!     cm_buf_t *bp;
! 
!     i = BUF_HASH(&scp->fid, offsetp);
!     for(bp = buf_hashTablepp[i]; bp; bp=bp->hashp) {
!         if (cm_FidCmp(&scp->fid, &bp->fid) == 0
!              && offsetp->LowPart == bp->offset.LowPart
!              && offsetp->HighPart == bp->offset.HighPart) {
!             bp->refCount++;
!             break;
          }
+     }
          
!     /* return whatever we found, if anything */
!     return bp;
  }
  
  /* find a buffer with offset *offsetp for vnode *scp.  Called
***************
*** 586,599 ****
   */
  cm_buf_t *buf_Find(struct cm_scache *scp, osi_hyper_t *offsetp)
  {
! 	cm_buf_t *bp;
  
! 	lock_ObtainWrite(&buf_globalLock);
! 	bp = buf_LockedFind(scp, offsetp);
! 	lock_ReleaseWrite(&buf_globalLock);
  
! 	return bp;
! }
  
  /* start cleaning I/O on this buffer.  Buffer must be write locked, and is returned
   * write-locked.
--- 589,602 ----
   */
  cm_buf_t *buf_Find(struct cm_scache *scp, osi_hyper_t *offsetp)
  {
!     cm_buf_t *bp;
  
!     lock_ObtainWrite(&buf_globalLock);
!     bp = buf_LockedFind(scp, offsetp);
!     lock_ReleaseWrite(&buf_globalLock);
  
!     return bp;
! }       
  
  /* start cleaning I/O on this buffer.  Buffer must be write locked, and is returned
   * write-locked.
***************
*** 604,640 ****
   */
  void buf_LockedCleanAsync(cm_buf_t *bp, cm_req_t *reqp)
  {
! 	long code;
  
! 	code = 0;
! 	while ((bp->flags & (CM_BUF_WRITING | CM_BUF_DIRTY)) == CM_BUF_DIRTY) {
!         	lock_ReleaseMutex(&bp->mx);
! 
! 	        code = (*cm_buf_opsp->Writep)(&bp->fid, &bp->offset,
! 						buf_bufferSize, 0, bp->userp,
! 						reqp);
                  
!                 lock_ObtainMutex(&bp->mx);
!                 if (code) break;
  
  #ifdef DISKCACHE95
!                 /* Disk cache support */
!                 /* write buffer to disk cache (synchronous for now) */
!                 diskcache_Update(bp->dcp, bp->datap, buf_bufferSize, bp->dataVersion);
  #endif /* DISKCACHE95 */
! 	};
  
!         /* do logging after call to GetLastError, or else */
! 	osi_Log2(buf_logp, "buf_CleanAsync starts I/O on 0x%x, done=%d", bp, code);
          
! 	/* if someone was waiting for the I/O that just completed or failed,
!          * wake them up.
!          */
!         if (bp->flags & CM_BUF_WAITING) {
! 		/* turn off flags and wakeup users */
!                 bp->flags &= ~CM_BUF_WAITING;
!                 osi_Wakeup((long) bp);
!         }
  }
  
  /* Called with a zero-ref count buffer and with the buf_globalLock write locked.
--- 607,644 ----
   */
  void buf_LockedCleanAsync(cm_buf_t *bp, cm_req_t *reqp)
  {
!     long code;
! 
!     code = 0;
!     while ((bp->flags & (CM_BUF_WRITING | CM_BUF_DIRTY)) == CM_BUF_DIRTY) {
!         lock_ReleaseMutex(&bp->mx);
  
!         code = (*cm_buf_opsp->Writep)(&bp->fid, &bp->offset,
!                                        buf_bufferSize, 0, bp->userp,
!                                        reqp);
                  
!         lock_ObtainMutex(&bp->mx);
!         if (code) 
!             break;
  
  #ifdef DISKCACHE95
!         /* Disk cache support */
!         /* write buffer to disk cache (synchronous for now) */
!         diskcache_Update(bp->dcp, bp->datap, buf_bufferSize, bp->dataVersion);
  #endif /* DISKCACHE95 */
!     };
  
!     /* do logging after call to GetLastError, or else */
!     osi_Log2(buf_logp, "buf_CleanAsync starts I/O on 0x%x, done=%d", bp, code);
          
!     /* if someone was waiting for the I/O that just completed or failed,
!      * wake them up.
!      */
!     if (bp->flags & CM_BUF_WAITING) {
!         /* turn off flags and wakeup users */
!         bp->flags &= ~CM_BUF_WAITING;
!         osi_Wakeup((long) bp);
!     }
  }
  
  /* Called with a zero-ref count buffer and with the buf_globalLock write locked.
***************
*** 643,705 ****
   */
  void buf_Recycle(cm_buf_t *bp)
  {
! 	int i;
!         cm_buf_t **lbpp;
!         cm_buf_t *tbp;
! 	cm_buf_t *prevBp, *nextBp;
! 
! 	/* if we get here, we know that the buffer still has a 0 ref count,
! 	 * and that it is clean and has no currently pending I/O.  This is
! 	 * the dude to return.
! 	 * Remember that as long as the ref count is 0, we know that we won't
! 	 * have any lock conflicts, so we can grab the buffer lock out of
! 	 * order in the locking hierarchy.
! 	 */
      osi_Log2( buf_logp, "buf_Recycle recycles 0x%x, off 0x%x",
! 		bp, bp->offset.LowPart);
  
! 	osi_assert(bp->refCount == 0);
! 	osi_assert(!(bp->flags & (CM_BUF_READING | CM_BUF_WRITING | CM_BUF_DIRTY)));
! 	lock_AssertWrite(&buf_globalLock);
! 
! 	if (bp->flags & CM_BUF_INHASH) {
! 		/* Remove from hash */
! 
! 		i = BUF_HASH(&bp->fid, &bp->offset);
! 		lbpp = &(buf_hashTablepp[i]);
! 		for(tbp = *lbpp; tbp; lbpp = &tbp->hashp, tbp = *lbpp) {
! 			if (tbp == bp) break;
! 		}
! 
! 		/* we better find it */
! 		osi_assertx(tbp != NULL, "buf_GetNewLocked: hash table screwup");
! 
! 		*lbpp = bp->hashp;	/* hash out */
! 
! 		/* Remove from file hash */
! 
! 		i = BUF_FILEHASH(&bp->fid);
! 		prevBp = bp->fileHashBackp;
! 		nextBp = bp->fileHashp;
! 		if (prevBp)
! 			prevBp->fileHashp = nextBp;
! 		else
! 			buf_fileHashTablepp[i] = nextBp;
! 		if (nextBp)
! 			nextBp->fileHashBackp = prevBp;
  
! 		bp->flags &= ~CM_BUF_INHASH;
! 	}
!                         
! 	/* bump the soft reference counter now, to invalidate softRefs; no
! 	 * wakeup is required since people don't sleep waiting for this
! 	 * counter to change.
! 	 */
! 	bp->idCounter++;
  
! 	/* make the fid unrecognizable */
!         memset(&bp->fid, 0, sizeof(bp->fid));
! }
  
  /* recycle a buffer, removing it from the free list, hashing in its new identity
   * and returning it write-locked so that no one can use it.  Called without
--- 647,709 ----
   */
  void buf_Recycle(cm_buf_t *bp)
  {
!     int i;
!     cm_buf_t **lbpp;
!     cm_buf_t *tbp;
!     cm_buf_t *prevBp, *nextBp;
! 
!     /* if we get here, we know that the buffer still has a 0 ref count,
!      * and that it is clean and has no currently pending I/O.  This is
!      * the dude to return.
!      * Remember that as long as the ref count is 0, we know that we won't
!      * have any lock conflicts, so we can grab the buffer lock out of
!      * order in the locking hierarchy.
!      */
      osi_Log2( buf_logp, "buf_Recycle recycles 0x%x, off 0x%x",
!               bp, bp->offset.LowPart);
  
!     osi_assert(bp->refCount == 0);
!     osi_assert(!(bp->flags & (CM_BUF_READING | CM_BUF_WRITING | CM_BUF_DIRTY)));
!     lock_AssertWrite(&buf_globalLock);
! 
!     if (bp->flags & CM_BUF_INHASH) {
!         /* Remove from hash */
! 
!         i = BUF_HASH(&bp->fid, &bp->offset);
!         lbpp = &(buf_hashTablepp[i]);
!         for(tbp = *lbpp; tbp; lbpp = &tbp->hashp, tbp = *lbpp) {
!             if (tbp == bp) break;
!         }
  
!         /* we better find it */
!         osi_assertx(tbp != NULL, "buf_GetNewLocked: hash table screwup");
  
!         *lbpp = bp->hashp;	/* hash out */
! 
!         /* Remove from file hash */
! 
!         i = BUF_FILEHASH(&bp->fid);
!         prevBp = bp->fileHashBackp;
!         nextBp = bp->fileHashp;
!         if (prevBp)
!             prevBp->fileHashp = nextBp;
!         else
!             buf_fileHashTablepp[i] = nextBp;
!         if (nextBp)
!             nextBp->fileHashBackp = prevBp;
! 
!         bp->flags &= ~CM_BUF_INHASH;
!     }
! 
!     /* bump the soft reference counter now, to invalidate softRefs; no
!      * wakeup is required since people don't sleep waiting for this
!      * counter to change.
!      */
!     bp->idCounter++;
! 
!     /* make the fid unrecognizable */
!     memset(&bp->fid, 0, sizeof(bp->fid));
! }       
  
  /* recycle a buffer, removing it from the free list, hashing in its new identity
   * and returning it write-locked so that no one can use it.  Called without
***************
*** 714,866 ****
   */
  long buf_GetNewLocked(struct cm_scache *scp, osi_hyper_t *offsetp, cm_buf_t **bufpp)
  {
! 	cm_buf_t *bp;		/* buffer we're dealing with */
! 	cm_buf_t *nextBp;	/* next buffer in file hash chain */
!         long i;			/* temp */
! 	cm_req_t req;
! 
! 	cm_InitReq(&req);	/* just in case */
! 
! 	while(1) {
! retry:
! 		lock_ObtainWrite(&buf_globalLock);
! 		/* check to see if we lost the race */
! 		if (scp) {
! 			if (bp = buf_LockedFind(scp, offsetp)) {
! 				bp->refCount--;
! 				lock_ReleaseWrite(&buf_globalLock);
! 	                	return CM_BUF_EXISTS;
! 	                }
! 		}
!                 
! 		/* for debugging, assert free list isn't empty, although we
! 		 * really should try waiting for a running tranasction to finish
! 		 * instead of this; or better, we should have a transaction
! 		 * throttler prevent us from entering this situation.
!                  */
!                 osi_assertx(buf_freeListEndp != NULL, "buf_GetNewLocked: no free buffers");
  
! 		/* look at all buffers in free list, some of which may temp.
! 		 * have high refcounts and which then should be skipped,
! 		 * starting cleaning I/O for those which are dirty.  If we find
! 		 * a clean buffer, we rehash it, lock it and return it.
!                  */
!                 for(bp = buf_freeListEndp; bp; bp=(cm_buf_t *) osi_QPrev(&bp->q)) {
! 			/* check to see if it really has zero ref count.  This
! 			 * code can bump refcounts, at least, so it may not be
! 			 * zero.
!                          */
!                         if (bp->refCount > 0) continue;
!                         
! 			/* we don't have to lock buffer itself, since the ref
! 			 * count is 0 and we know it will stay zero as long as
! 			 * we hold the global lock.
!                          */
! 
! 			/* don't recycle someone in our own chunk */
! 			if (!cm_FidCmp(&bp->fid, &scp->fid)
! 			    && (bp->offset.LowPart & (-cm_chunkSize))
! 				  == (offsetp->LowPart & (-cm_chunkSize)))
! 				continue;
! 
! 			/* if this page is being filled (!) or cleaned, see if
! 			 * the I/O has completed.  If not, skip it, otherwise
! 			 * do the final processing for the I/O.
!                          */
!                         if (bp->flags & (CM_BUF_READING | CM_BUF_WRITING)) {
! 				/* probably shouldn't do this much work while
! 				 * holding the big lock?  Watch for contention
! 				 * here.
!                                  */
!                                 continue;
!                         }
!                         
!                         if (bp->flags & CM_BUF_DIRTY) {
! 				/* if the buffer is dirty, start cleaning it and
! 				 * move on to the next buffer.  We do this with
! 				 * just the lock required to minimize contention
! 				 * on the big lock.
!                                  */
! 				bp->refCount++;
!                                 lock_ReleaseWrite(&buf_globalLock);
! 
! 				/* grab required lock and clean; this only
! 				 * starts the I/O.  By the time we're back,
! 				 * it'll still be marked dirty, but it will also
! 				 * have the WRITING flag set, so we won't get
! 				 * back here.
! 				 */
!                                	buf_CleanAsync(bp, &req);
!                                 
!                                 /* now put it back and go around again */
! 				buf_Release(bp);
!                                 goto retry;
!                         }
!                         
!                         /* if we get here, we know that the buffer still has a 0
! 			 * ref count, and that it is clean and has no currently
! 			 * pending I/O.  This is the dude to return.
!                          * Remember that as long as the ref count is 0, we know
! 			 * that we won't have any lock conflicts, so we can grab
! 			 * the buffer lock out of order in the locking hierarchy.
!                          */
!                         buf_Recycle(bp);
! 
! 			/* clean up junk flags */
! 			bp->flags &= ~(CM_BUF_EOF | CM_BUF_ERROR);
! 			bp->dataVersion = -1;	/* unknown so far */
! 
! 			/* now hash in as our new buffer, and give it the
! 			 * appropriate label, if requested.
!                          */
! 			if (scp) {
! 				bp->flags |= CM_BUF_INHASH;
! 				bp->fid = scp->fid;
! 				bp->offset = *offsetp;
! 				i = BUF_HASH(&scp->fid, offsetp);
! 				bp->hashp = buf_hashTablepp[i];
! 				buf_hashTablepp[i] = bp;
! 				i = BUF_FILEHASH(&scp->fid);
! 				nextBp = buf_fileHashTablepp[i];
! 				bp->fileHashp = nextBp;
! 				bp->fileHashBackp = NULL;
! 				if (nextBp)
! 					nextBp->fileHashBackp = bp;
! 				buf_fileHashTablepp[i] = bp;
! 			}
                          
! 			/* prepare to return it.  Start by giving it a good
! 			 * refcount */
! 			bp->refCount = 1;
                          
! 			/* and since it has a non-zero ref count, we should move
! 			 * it from the lru queue.  It better be still there,
! 			 * since we've held the global (big) lock since we found
! 			 * it there.
! 			 */
! 			osi_assertx(bp->flags & CM_BUF_INLRU,
! 				    "buf_GetNewLocked: LRU screwup");
! 			if (buf_freeListEndp == bp) {
! 				/* we're the last guy in this queue, so maintain it */
! 				buf_freeListEndp = (cm_buf_t *) osi_QPrev(&bp->q);
! 			}
! 			osi_QRemove((osi_queue_t **) &buf_freeListp, &bp->q);
! 			bp->flags &= ~CM_BUF_INLRU;
                          
! 			/* finally, grab the mutex so that people don't use it
! 			 * before the caller fills it with data.  Again, no one	
! 			 * should have been able to get to this dude to lock it.
! 			 */
! 			osi_assertx(lock_TryMutex(&bp->mx),
! 				    "buf_GetNewLocked: TryMutex failed");
! 
!                         lock_ReleaseWrite(&buf_globalLock);
!                         *bufpp = bp;
!                         return 0;
!                 } /* for all buffers in lru queue */
! 		lock_ReleaseWrite(&buf_globalLock);
!         }	/* while loop over everything */
!         /* not reached */
  } /* the proc */
  
  /* get a page, returning it held but unlocked.  Doesn't fill in the page
--- 718,871 ----
   */
  long buf_GetNewLocked(struct cm_scache *scp, osi_hyper_t *offsetp, cm_buf_t **bufpp)
  {
!     cm_buf_t *bp;		/* buffer we're dealing with */
!     cm_buf_t *nextBp;	/* next buffer in file hash chain */
!     long i;			/* temp */
!     cm_req_t req;
  
!     cm_InitReq(&req);	/* just in case */
! 
!     while(1) {
!       retry:
!         lock_ObtainWrite(&buf_globalLock);
!         /* check to see if we lost the race */
!         if (scp) {
!             if (bp = buf_LockedFind(scp, offsetp)) {
!                 bp->refCount--;
!                 lock_ReleaseWrite(&buf_globalLock);
!                 return CM_BUF_EXISTS;
!             }
!         }
! 
!         /* for debugging, assert free list isn't empty, although we
!          * really should try waiting for a running tranasction to finish
!          * instead of this; or better, we should have a transaction
!          * throttler prevent us from entering this situation.
!          */
!         osi_assertx(buf_freeListEndp != NULL, "buf_GetNewLocked: no free buffers");
! 
!         /* look at all buffers in free list, some of which may temp.
!          * have high refcounts and which then should be skipped,
!          * starting cleaning I/O for those which are dirty.  If we find
!          * a clean buffer, we rehash it, lock it and return it.
!          */
!         for(bp = buf_freeListEndp; bp; bp=(cm_buf_t *) osi_QPrev(&bp->q)) {
!             /* check to see if it really has zero ref count.  This
!              * code can bump refcounts, at least, so it may not be
!              * zero.
!              */
!             if (bp->refCount > 0) 
!                 continue;
                          
!             /* we don't have to lock buffer itself, since the ref
!              * count is 0 and we know it will stay zero as long as
!              * we hold the global lock.
!              */
! 
!             /* don't recycle someone in our own chunk */
!             if (!cm_FidCmp(&bp->fid, &scp->fid)
!                  && (bp->offset.LowPart & (-cm_chunkSize))
!                  == (offsetp->LowPart & (-cm_chunkSize)))
!                 continue;
! 
!             /* if this page is being filled (!) or cleaned, see if
!              * the I/O has completed.  If not, skip it, otherwise
!              * do the final processing for the I/O.
!              */
!             if (bp->flags & (CM_BUF_READING | CM_BUF_WRITING)) {
!                 /* probably shouldn't do this much work while
!                  * holding the big lock?  Watch for contention
!                  * here.
!                  */
!                 continue;
!             }
                          
!             if (bp->flags & CM_BUF_DIRTY) {
!                 /* if the buffer is dirty, start cleaning it and
!                  * move on to the next buffer.  We do this with
!                  * just the lock required to minimize contention
!                  * on the big lock.
!                  */
!                 bp->refCount++;
!                 lock_ReleaseWrite(&buf_globalLock);
! 
!                 /* grab required lock and clean; this only
!                  * starts the I/O.  By the time we're back,
!                  * it'll still be marked dirty, but it will also
!                  * have the WRITING flag set, so we won't get
!                  * back here.
!                  */
!                 buf_CleanAsync(bp, &req);
! 
!                 /* now put it back and go around again */
!                 buf_Release(bp);
!                 goto retry;
!             }
! 
!             /* if we get here, we know that the buffer still has a 0
!              * ref count, and that it is clean and has no currently
!              * pending I/O.  This is the dude to return.
!              * Remember that as long as the ref count is 0, we know
!              * that we won't have any lock conflicts, so we can grab
!              * the buffer lock out of order in the locking hierarchy.
!              */
!             buf_Recycle(bp);
! 
!             /* clean up junk flags */
!             bp->flags &= ~(CM_BUF_EOF | CM_BUF_ERROR);
!             bp->dataVersion = -1;	/* unknown so far */
! 
!             /* now hash in as our new buffer, and give it the
!              * appropriate label, if requested.
!              */
!             if (scp) {
!                 bp->flags |= CM_BUF_INHASH;
!                 bp->fid = scp->fid;
!                 bp->offset = *offsetp;
!                 i = BUF_HASH(&scp->fid, offsetp);
!                 bp->hashp = buf_hashTablepp[i];
!                 buf_hashTablepp[i] = bp;
!                 i = BUF_FILEHASH(&scp->fid);
!                 nextBp = buf_fileHashTablepp[i];
!                 bp->fileHashp = nextBp;
!                 bp->fileHashBackp = NULL;
!                 if (nextBp)
!                     nextBp->fileHashBackp = bp;
!                 buf_fileHashTablepp[i] = bp;
!             }
! 
!             /* prepare to return it.  Start by giving it a good
!              * refcount */
!             bp->refCount = 1;
                          
!             /* and since it has a non-zero ref count, we should move
!              * it from the lru queue.  It better be still there,
!              * since we've held the global (big) lock since we found
!              * it there.
!              */
!             osi_assertx(bp->flags & CM_BUF_INLRU,
!                          "buf_GetNewLocked: LRU screwup");
!             if (buf_freeListEndp == bp) {
!                 /* we're the last guy in this queue, so maintain it */
!                 buf_freeListEndp = (cm_buf_t *) osi_QPrev(&bp->q);
!             }
!             osi_QRemove((osi_queue_t **) &buf_freeListp, &bp->q);
!             bp->flags &= ~CM_BUF_INLRU;
! 
!             /* finally, grab the mutex so that people don't use it
!              * before the caller fills it with data.  Again, no one	
!              * should have been able to get to this dude to lock it.
!              */
!             osi_assertx(lock_TryMutex(&bp->mx),
!                          "buf_GetNewLocked: TryMutex failed");
! 
!             lock_ReleaseWrite(&buf_globalLock);
!             *bufpp = bp;
!             return 0;
!         } /* for all buffers in lru queue */
!         lock_ReleaseWrite(&buf_globalLock);
!     }	/* while loop over everything */
!     /* not reached */
  } /* the proc */
  
  /* get a page, returning it held but unlocked.  Doesn't fill in the page
***************
*** 868,1052 ****
   */
  long buf_GetNew(struct cm_scache *scp, osi_hyper_t *offsetp, cm_buf_t **bufpp)
  {
! 	cm_buf_t *bp;
!         long code;
!         osi_hyper_t pageOffset;
!         int created;
! 
! 	created = 0;
!         pageOffset.HighPart = offsetp->HighPart;
!         pageOffset.LowPart = offsetp->LowPart & ~(buf_bufferSize-1);
! 	while (1) {
! 		lock_ObtainWrite(&buf_globalLock);
! 		bp = buf_LockedFind(scp, &pageOffset);
! 		lock_ReleaseWrite(&buf_globalLock);
!                 if (bp) {
! 			/* lock it and break out */
!                 	lock_ObtainMutex(&bp->mx);
!                         break;
!                 }
!                 
!                 /* otherwise, we have to create a page */
!                 code = buf_GetNewLocked(scp, &pageOffset, &bp);
  
! 		/* check if the buffer was created in a race condition branch.
! 		 * If so, go around so we can hold a reference to it. 
!                  */
! 		if (code == CM_BUF_EXISTS) continue;
!                 
! 		/* something else went wrong */
!                 if (code != 0) return code;
!                 
!                 /* otherwise, we have a locked buffer that we just created */
!                 created = 1;
!                 break;
!         } /* big while loop */
!         
! 	/* wait for reads */
! 	if (bp->flags & CM_BUF_READING)
!         	buf_WaitIO(bp);
  
!         /* once it has been read once, we can unlock it and return it, still
! 	 * with its refcount held.
           */
!         lock_ReleaseMutex(&bp->mx);
!         *bufpp = bp;
!         osi_Log3(buf_logp, "buf_GetNew returning bp 0x%x for file 0x%x, offset 0x%x",
!         	bp, (long) scp, offsetp->LowPart);
!         return 0;
  }
  
  /* get a page, returning it held but unlocked.  Make sure it is complete */
  long buf_Get(struct cm_scache *scp, osi_hyper_t *offsetp, cm_buf_t **bufpp)
  {
! 	cm_buf_t *bp;
!         long code;
!         osi_hyper_t pageOffset;
!         unsigned long tcount;
!         int created;
  #ifdef DISKCACHE95
!         cm_diskcache_t *dcp;
  #endif /* DISKCACHE95 */
  
! 	created = 0;
!         pageOffset.HighPart = offsetp->HighPart;
!         pageOffset.LowPart = offsetp->LowPart & ~(buf_bufferSize-1);
! 	while (1) {
! 		lock_ObtainWrite(&buf_globalLock);
! 		bp = buf_LockedFind(scp, &pageOffset);
! 		lock_ReleaseWrite(&buf_globalLock);
!                 if (bp) {
! 			/* lock it and break out */
!                 	lock_ObtainMutex(&bp->mx);
!                         break;
  
  #ifdef DISKCACHE95
!                         /* touch disk chunk to update LRU info */
!                         diskcache_Touch(bp->dcp);
  #endif /* DISKCACHE95 */
!                 }
!                 
!                 /* otherwise, we have to create a page */
!                 code = buf_GetNewLocked(scp, &pageOffset, &bp);
  
! 		/* check if the buffer was created in a race condition branch.
! 		 * If so, go around so we can hold a reference to it. 
!                  */
! 		if (code == CM_BUF_EXISTS) continue;
!                 
! 		/* something else went wrong */
!                 if (code != 0) return code;
!                 
!                 /* otherwise, we have a locked buffer that we just created */
!                 created = 1;
!                 break;
!         } /* big while loop */
!         
!         /* if we get here, we have a locked buffer that may have just been
! 	 * created, in which case it needs to be filled with data.
           */
!         if (created) {
! 		/* load the page; freshly created pages should be idle */
! 		osi_assert(!(bp->flags & (CM_BUF_READING | CM_BUF_WRITING)));
  
! 		/* setup offset, event */
  #ifndef DJGPP  /* doesn't seem to be used */
! 	        bp->over.Offset = bp->offset.LowPart;
! 	        bp->over.OffsetHigh = bp->offset.HighPart;
  #endif /* !DJGPP */
  
! 		/* start the I/O; may drop lock */
!                 bp->flags |= CM_BUF_READING;
!                	code = (*cm_buf_opsp->Readp)(bp, buf_bufferSize, &tcount, NULL);
  
  #ifdef DISKCACHE95
!                 code = diskcache_Get(&bp->fid, &bp->offset, bp->datap, buf_bufferSize, &bp->dataVersion, &tcount, &dcp);
!                 bp->dcp = dcp;    /* pointer to disk cache struct. */
  #endif /* DISKCACHE95 */
  
! 		if (code != 0) {
! 			/* failure or queued */
  #ifndef DJGPP   /* cm_bufRead always returns 0 */
!                         if (code != ERROR_IO_PENDING) {
  #endif
! 				bp->error = code;
!                                 bp->flags |= CM_BUF_ERROR;
!                                 bp->flags &= ~CM_BUF_READING;
!                                 if (bp->flags & CM_BUF_WAITING) {
! 					bp->flags &= ~CM_BUF_WAITING;
!                                         osi_Wakeup((long) bp);
!                                 }
! 				lock_ReleaseMutex(&bp->mx);
!                                 buf_Release(bp);
!                                 return code;
  #ifndef DJGPP
!                         }
  #endif
!                 } else {
! 	                /* otherwise, I/O completed instantly and we're done, except
!                          * for padding the xfr out with 0s and checking for EOF
!                          */
! 			if (tcount < (unsigned long) buf_bufferSize) {
! 				memset(bp->datap+tcount, 0, buf_bufferSize - tcount);
!                                 if (tcount == 0)
!                                 	bp->flags |= CM_BUF_EOF;
!                         }
! 			bp->flags &= ~CM_BUF_READING;
! 			if (bp->flags & CM_BUF_WAITING) {
! 				bp->flags &= ~CM_BUF_WAITING;
!                                 osi_Wakeup((long) bp);
!                         }
!                 }
!                         
!         } /* if created */
!         
! 	/* wait for reads, either that which we started above, or that someone
! 	 * else started.  We don't care if we return a buffer being cleaned.
!          */
! 	if (bp->flags & CM_BUF_READING)
!         	buf_WaitIO(bp);
  
!         /* once it has been read once, we can unlock it and return it, still
! 	 * with its refcount held.
!          */
!         lock_ReleaseMutex(&bp->mx);
!         *bufpp = bp;
  
! 	/* now remove from queue; will be put in at the head (farthest from
! 	 * being recycled) when we're done in buf_Release.
!          */
!         lock_ObtainWrite(&buf_globalLock);
! 	if (bp->flags & CM_BUF_INLRU) {
! 		if (buf_freeListEndp == bp)
!                 	buf_freeListEndp = (cm_buf_t *) osi_QPrev(&bp->q);
! 		osi_QRemove((osi_queue_t **) &buf_freeListp, &bp->q);
!                 bp->flags &= ~CM_BUF_INLRU;
!         }
!         lock_ReleaseWrite(&buf_globalLock);
  
!         osi_Log3(buf_logp, "buf_Get returning bp 0x%x for file 0x%x, offset 0x%x",
!         	bp, (long) scp, offsetp->LowPart);
!         return 0;
  }
  
  /* count # of elements in the free list;
--- 873,1061 ----
   */
  long buf_GetNew(struct cm_scache *scp, osi_hyper_t *offsetp, cm_buf_t **bufpp)
  {
!     cm_buf_t *bp;
!     long code;
!     osi_hyper_t pageOffset;
!     int created;
! 
!     created = 0;
!     pageOffset.HighPart = offsetp->HighPart;
!     pageOffset.LowPart = offsetp->LowPart & ~(buf_bufferSize-1);
!     while (1) {
!         lock_ObtainWrite(&buf_globalLock);
!         bp = buf_LockedFind(scp, &pageOffset);
!         lock_ReleaseWrite(&buf_globalLock);
!         if (bp) {
!             /* lock it and break out */
!             lock_ObtainMutex(&bp->mx);
!             break;
!         }
  
!         /* otherwise, we have to create a page */
!         code = buf_GetNewLocked(scp, &pageOffset, &bp);
  
!         /* check if the buffer was created in a race condition branch.
!          * If so, go around so we can hold a reference to it. 
           */
!         if (code == CM_BUF_EXISTS) 
!             continue;
! 
!         /* something else went wrong */
!         if (code != 0) 
!             return code;
! 
!         /* otherwise, we have a locked buffer that we just created */
!         created = 1;
!         break;
!     } /* big while loop */
! 
!     /* wait for reads */
!     if (bp->flags & CM_BUF_READING)
!         buf_WaitIO(bp);
! 
!     /* once it has been read once, we can unlock it and return it, still
!      * with its refcount held.
!      */
!     lock_ReleaseMutex(&bp->mx);
!     *bufpp = bp;
!     osi_Log3(buf_logp, "buf_GetNew returning bp 0x%x for file 0x%x, offset 0x%x",
!               bp, (long) scp, offsetp->LowPart);
!     return 0;
  }
  
  /* get a page, returning it held but unlocked.  Make sure it is complete */
  long buf_Get(struct cm_scache *scp, osi_hyper_t *offsetp, cm_buf_t **bufpp)
  {
!     cm_buf_t *bp;
!     long code;
!     osi_hyper_t pageOffset;
!     unsigned long tcount;
!     int created;
  #ifdef DISKCACHE95
!     cm_diskcache_t *dcp;
  #endif /* DISKCACHE95 */
  
!     created = 0;
!     pageOffset.HighPart = offsetp->HighPart;
!     pageOffset.LowPart = offsetp->LowPart & ~(buf_bufferSize-1);
!     while (1) {
!         lock_ObtainWrite(&buf_globalLock);
!         bp = buf_LockedFind(scp, &pageOffset);
!         lock_ReleaseWrite(&buf_globalLock);
!         if (bp) {
!             /* lock it and break out */
!             lock_ObtainMutex(&bp->mx);
!             break;
  
  #ifdef DISKCACHE95
!             /* touch disk chunk to update LRU info */
!             diskcache_Touch(bp->dcp);
  #endif /* DISKCACHE95 */
!         }
  
!         /* otherwise, we have to create a page */
!         code = buf_GetNewLocked(scp, &pageOffset, &bp);
! 
!         /* check if the buffer was created in a race condition branch.
!          * If so, go around so we can hold a reference to it. 
           */
!         if (code == CM_BUF_EXISTS) 
!             continue;
! 
!         /* something else went wrong */
!         if (code != 0) 
!             return code;
!                 
!         /* otherwise, we have a locked buffer that we just created */
!         created = 1;
!         break;
!     } /* big while loop */
! 
!     /* if we get here, we have a locked buffer that may have just been
!      * created, in which case it needs to be filled with data.
!      */
!     if (created) {
!         /* load the page; freshly created pages should be idle */
!         osi_assert(!(bp->flags & (CM_BUF_READING | CM_BUF_WRITING)));
  
!         /* setup offset, event */
  #ifndef DJGPP  /* doesn't seem to be used */
!         bp->over.Offset = bp->offset.LowPart;
!         bp->over.OffsetHigh = bp->offset.HighPart;
  #endif /* !DJGPP */
  
!         /* start the I/O; may drop lock */
!         bp->flags |= CM_BUF_READING;
!         code = (*cm_buf_opsp->Readp)(bp, buf_bufferSize, &tcount, NULL);
  
  #ifdef DISKCACHE95
!         code = diskcache_Get(&bp->fid, &bp->offset, bp->datap, buf_bufferSize, &bp->dataVersion, &tcount, &dcp);
!         bp->dcp = dcp;    /* pointer to disk cache struct. */
  #endif /* DISKCACHE95 */
  
!         if (code != 0) {
!             /* failure or queued */
  #ifndef DJGPP   /* cm_bufRead always returns 0 */
!             if (code != ERROR_IO_PENDING) {
  #endif
!                 bp->error = code;
!                 bp->flags |= CM_BUF_ERROR;
!                 bp->flags &= ~CM_BUF_READING;
!                 if (bp->flags & CM_BUF_WAITING) {
!                     bp->flags &= ~CM_BUF_WAITING;
!                     osi_Wakeup((long) bp);
!                 }
!                 lock_ReleaseMutex(&bp->mx);
!                 buf_Release(bp);
!                 return code;
  #ifndef DJGPP
!             }
  #endif
!         } else {
!             /* otherwise, I/O completed instantly and we're done, except
!              * for padding the xfr out with 0s and checking for EOF
!              */
!             if (tcount < (unsigned long) buf_bufferSize) {
!                 memset(bp->datap+tcount, 0, buf_bufferSize - tcount);
!                 if (tcount == 0)
!                     bp->flags |= CM_BUF_EOF;
!             }
!             bp->flags &= ~CM_BUF_READING;
!             if (bp->flags & CM_BUF_WAITING) {
!                 bp->flags &= ~CM_BUF_WAITING;
!                 osi_Wakeup((long) bp);
!             }
!         }
  
!     } /* if created */
  
!     /* wait for reads, either that which we started above, or that someone
!      * else started.  We don't care if we return a buffer being cleaned.
!      */
!     if (bp->flags & CM_BUF_READING)
!         buf_WaitIO(bp);
  
!     /* once it has been read once, we can unlock it and return it, still
!      * with its refcount held.
!      */
!     lock_ReleaseMutex(&bp->mx);
!     *bufpp = bp;
! 
!     /* now remove from queue; will be put in at the head (farthest from
!      * being recycled) when we're done in buf_Release.
!      */
!     lock_ObtainWrite(&buf_globalLock);
!     if (bp->flags & CM_BUF_INLRU) {
!         if (buf_freeListEndp == bp)
!             buf_freeListEndp = (cm_buf_t *) osi_QPrev(&bp->q);
!         osi_QRemove((osi_queue_t **) &buf_freeListp, &bp->q);
!         bp->flags &= ~CM_BUF_INLRU;
!     }
!     lock_ReleaseWrite(&buf_globalLock);
! 
!     osi_Log3(buf_logp, "buf_Get returning bp 0x%x for file 0x%x, offset 0x%x",
!               bp, (long) scp, offsetp->LowPart);
!     return 0;
  }
  
  /* count # of elements in the free list;
***************
*** 1056,1095 ****
   */
  long buf_CountFreeList(void)
  {
! 	long count;
!         cm_buf_t *bufp;
  
! 	count = 0;
! 	lock_ObtainRead(&buf_globalLock);
! 	for(bufp = buf_freeListp; bufp; bufp = (cm_buf_t *) osi_QNext(&bufp->q)) {
! 		/* if the buffer doesn't have an identity, or if the buffer
!                  * has been invalidate (by having its DV stomped upon), then
!                  * count it as free, since it isn't really being utilized.
!                  */
! 		if (!(bufp->flags & CM_BUF_INHASH) || bufp->dataVersion <= 0)
!                 	count++;
!         }
! 	lock_ReleaseRead(&buf_globalLock);
!         return count;
  }
  
  /* clean a buffer synchronously */
  void buf_CleanAsync(cm_buf_t *bp, cm_req_t *reqp)
  {
! 	lock_ObtainMutex(&bp->mx);
! 	buf_LockedCleanAsync(bp, reqp);
! 	lock_ReleaseMutex(&bp->mx);
! }
  
  /* wait for a buffer's cleaning to finish */
  void buf_CleanWait(cm_buf_t *bp)
  {
! 	lock_ObtainMutex(&bp->mx);
! 	if (bp->flags & CM_BUF_WRITING) {
! 		buf_WaitIO(bp);
!         }
! 	lock_ReleaseMutex(&bp->mx);
! }
  
  /* set the dirty flag on a buffer, and set associated write-ahead log,
   * if there is one.  Allow one to be added to a buffer, but not changed.
--- 1065,1104 ----
   */
  long buf_CountFreeList(void)
  {
!     long count;
!     cm_buf_t *bufp;
  
!     count = 0;
!     lock_ObtainRead(&buf_globalLock);
!     for(bufp = buf_freeListp; bufp; bufp = (cm_buf_t *) osi_QNext(&bufp->q)) {
!         /* if the buffer doesn't have an identity, or if the buffer
!          * has been invalidate (by having its DV stomped upon), then
!          * count it as free, since it isn't really being utilized.
!          */
!         if (!(bufp->flags & CM_BUF_INHASH) || bufp->dataVersion <= 0)
!             count++;
!     }       
!     lock_ReleaseRead(&buf_globalLock);
!     return count;
  }
  
  /* clean a buffer synchronously */
  void buf_CleanAsync(cm_buf_t *bp, cm_req_t *reqp)
  {
!     lock_ObtainMutex(&bp->mx);
!     buf_LockedCleanAsync(bp, reqp);
!     lock_ReleaseMutex(&bp->mx);
! }       
  
  /* wait for a buffer's cleaning to finish */
  void buf_CleanWait(cm_buf_t *bp)
  {
!     lock_ObtainMutex(&bp->mx);
!     if (bp->flags & CM_BUF_WRITING) {
!         buf_WaitIO(bp);
!     }
!     lock_ReleaseMutex(&bp->mx);
! }       
  
  /* set the dirty flag on a buffer, and set associated write-ahead log,
   * if there is one.  Allow one to be added to a buffer, but not changed.
***************
*** 1098,1112 ****
   */
  void buf_SetDirty(cm_buf_t *bp)
  {
! 	osi_assert(bp->refCount > 0);
  	
! 	osi_Log1(buf_logp, "buf_SetDirty 0x%x", bp);
  
!         /* set dirty bit */
! 	bp->flags |= CM_BUF_DIRTY;
  
! 	/* and turn off EOF flag, since it has associated data now */
!         bp->flags &= ~CM_BUF_EOF;
  }
  
  /* clean all buffers, reset log pointers and invalidate all buffers.
--- 1107,1121 ----
   */
  void buf_SetDirty(cm_buf_t *bp)
  {
!     osi_assert(bp->refCount > 0);
  	
!     osi_Log1(buf_logp, "buf_SetDirty 0x%x", bp);
  
!     /* set dirty bit */
!     bp->flags |= CM_BUF_DIRTY;
  
!     /* and turn off EOF flag, since it has associated data now */
!     bp->flags &= ~CM_BUF_EOF;
  }
  
  /* clean all buffers, reset log pointers and invalidate all buffers.
***************
*** 1131,1214 ****
   */
  long buf_CleanAndReset(void)
  {
! 	long i;
!         cm_buf_t *bp;
! 	cm_req_t req;
! 
! 	lock_ObtainWrite(&buf_globalLock);
!         for(i=0; i<buf_hashSize; i++) {
!         	for(bp = buf_hashTablepp[i]; bp; bp = bp->hashp) {
! 			bp->refCount++;
!                         lock_ReleaseWrite(&buf_globalLock);
! 			
!                         /* now no locks are held; clean buffer and go on */
! 			cm_InitReq(&req);
!                         buf_CleanAsync(bp, &req);
!                         buf_CleanWait(bp);
!                         
!                         /* relock and release buffer */
!                         lock_ObtainWrite(&buf_globalLock);
!                         buf_LockedRelease(bp);
!                 } /* over one bucket */
! 	}	/* for loop over all hash buckets */
!         
!         /* release locks */
! 	lock_ReleaseWrite(&buf_globalLock);
  
! 	/* and we're done */
!         return 0;
! }
  
  /* called without global lock being held, reserves buffers for callers
   * that need more than one held (not locked) at once.
   */
  void buf_ReserveBuffers(long nbuffers)
  {
! 	lock_ObtainWrite(&buf_globalLock);
! 	while (1) {
! 		if (buf_reservedBufs + nbuffers > buf_maxReservedBufs) {
! 			buf_reserveWaiting = 1;
!                         osi_Log1(buf_logp, "buf_ReserveBuffers waiting for %d bufs", nbuffers);
!                         osi_SleepW((long) &buf_reservedBufs, &buf_globalLock);
!                         lock_ObtainWrite(&buf_globalLock);
!                 }
!                 else {
! 			buf_reservedBufs += nbuffers;
!                 	break;
!                 }
          }
! 	lock_ReleaseWrite(&buf_globalLock);
  }
  
  int buf_TryReserveBuffers(long nbuffers)
  {
! 	int code;
  
! 	lock_ObtainWrite(&buf_globalLock);
! 	if (buf_reservedBufs + nbuffers > buf_maxReservedBufs) {
! 		code = 0;
! 	}
! 	else {
! 		buf_reservedBufs += nbuffers;
!                 code = 1;
! 	}
! 	lock_ReleaseWrite(&buf_globalLock);
! 	return code;
! }
  
  /* called without global lock held, releases reservation held by
   * buf_ReserveBuffers.
   */
  void buf_UnreserveBuffers(long nbuffers)
  {
! 	lock_ObtainWrite(&buf_globalLock);
! 	buf_reservedBufs -= nbuffers;
!         if (buf_reserveWaiting) {
! 		buf_reserveWaiting = 0;
!                 osi_Wakeup((long) &buf_reservedBufs);
!         }
! 	lock_ReleaseWrite(&buf_globalLock);
! }
  
  /* truncate the buffers past sizep, zeroing out the page, if we don't
   * end on a page boundary.
--- 1140,1223 ----
   */
  long buf_CleanAndReset(void)
  {
!     long i;
!     cm_buf_t *bp;
!     cm_req_t req;
  
!     lock_ObtainWrite(&buf_globalLock);
!     for(i=0; i<buf_hashSize; i++) {
!         for(bp = buf_hashTablepp[i]; bp; bp = bp->hashp) {
!             bp->refCount++;
!             lock_ReleaseWrite(&buf_globalLock);
! 
!             /* now no locks are held; clean buffer and go on */
!             cm_InitReq(&req);
!             buf_CleanAsync(bp, &req);
!             buf_CleanWait(bp);
! 
!             /* relock and release buffer */
!             lock_ObtainWrite(&buf_globalLock);
!             buf_LockedRelease(bp);
!         } /* over one bucket */
!     }	/* for loop over all hash buckets */
! 
!     /* release locks */
!     lock_ReleaseWrite(&buf_globalLock);
! 
!     /* and we're done */
!     return 0;
! }       
  
  /* called without global lock being held, reserves buffers for callers
   * that need more than one held (not locked) at once.
   */
  void buf_ReserveBuffers(long nbuffers)
  {
!     lock_ObtainWrite(&buf_globalLock);
!     while (1) {
!         if (buf_reservedBufs + nbuffers > buf_maxReservedBufs) {
!             buf_reserveWaiting = 1;
!             osi_Log1(buf_logp, "buf_ReserveBuffers waiting for %d bufs", nbuffers);
!             osi_SleepW((long) &buf_reservedBufs, &buf_globalLock);
!             lock_ObtainWrite(&buf_globalLock);
          }
!         else {
!             buf_reservedBufs += nbuffers;
!             break;
!         }
!     }
!     lock_ReleaseWrite(&buf_globalLock);
  }
  
  int buf_TryReserveBuffers(long nbuffers)
  {
!     int code;
  
!     lock_ObtainWrite(&buf_globalLock);
!     if (buf_reservedBufs + nbuffers > buf_maxReservedBufs) {
!         code = 0;
!     }
!     else {
!         buf_reservedBufs += nbuffers;
!         code = 1;
!     }
!     lock_ReleaseWrite(&buf_globalLock);
!     return code;
! }       
  
  /* called without global lock held, releases reservation held by
   * buf_ReserveBuffers.
   */
  void buf_UnreserveBuffers(long nbuffers)
  {
!     lock_ObtainWrite(&buf_globalLock);
!     buf_reservedBufs -= nbuffers;
!     if (buf_reserveWaiting) {
!         buf_reserveWaiting = 0;
!         osi_Wakeup((long) &buf_reservedBufs);
!     }
!     lock_ReleaseWrite(&buf_globalLock);
! }       
  
  /* truncate the buffers past sizep, zeroing out the page, if we don't
   * end on a page boundary.
***************
*** 1216,1434 ****
   * Requires cm_bufCreateLock to be write locked.
   */
  long buf_Truncate(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
! 	osi_hyper_t *sizep)
  {
! 	cm_buf_t *bufp;
! 	cm_buf_t *nbufp;			/* next buffer, if didRelease */
!         osi_hyper_t bufEnd;
!         long code;
!         long bufferPos;
!         int didRelease;
! 	long i;
!         
! 	/* assert that cm_bufCreateLock is held in write mode */
!         lock_AssertWrite(&scp->bufCreateLock);
! 
! 	i = BUF_FILEHASH(&scp->fid);
  
! 	lock_ObtainWrite(&buf_globalLock);
! 	bufp = buf_fileHashTablepp[i];
! 	if (bufp == NULL) {
! 		lock_ReleaseWrite(&buf_globalLock);
! 		return 0;
! 	}
! 
!         bufp->refCount++;
! 	lock_ReleaseWrite(&buf_globalLock);
!         for(; bufp; bufp = nbufp) {
!                 didRelease = 0;
! 		lock_ObtainMutex(&bufp->mx);
! 
! 		bufEnd.HighPart = 0;
!                 bufEnd.LowPart = buf_bufferSize;
!                 bufEnd = LargeIntegerAdd(bufEnd, bufp->offset);
! 
! 		if (cm_FidCmp(&bufp->fid, &scp->fid) == 0 &&
!                 	LargeIntegerLessThan(*sizep, bufEnd)) {
! 	                buf_WaitIO(bufp);
! 		}
! 	        lock_ObtainMutex(&scp->mx);
  	
! 		/* make sure we have a callback (so we have the right value for
! 		 * the length), and wait for it to be safe to do a truncate.
! 	         */
! 		code = cm_SyncOp(scp, bufp, userp, reqp, 0,
! 				 CM_SCACHESYNC_NEEDCALLBACK
!                 		 | CM_SCACHESYNC_GETSTATUS
!                 		 | CM_SCACHESYNC_SETSIZE
! 				 | CM_SCACHESYNC_BUFLOCKED);
! 		/* if we succeeded in our locking, and this applies to the right
! 		 * file, and the truncate request overlaps the buffer either
! 		 * totally or partially, then do something.
!                  */
! 		if (code == 0 && cm_FidCmp(&bufp->fid, &scp->fid) == 0
!                 	&& LargeIntegerLessThan(*sizep, bufEnd)) {
!                         
!                         lock_ObtainWrite(&buf_globalLock);
  
! 			/* destroy the buffer, turning off its dirty bit, if
! 			 * we're truncating the whole buffer.  Otherwise, set
! 			 * the dirty bit, and clear out the tail of the buffer
! 			 * if we just overlap some.
!                          */
!                         if (LargeIntegerLessThanOrEqualTo(*sizep, bufp->offset)) {
! 				/* truncating the entire page */
!                                 bufp->flags &= ~CM_BUF_DIRTY;
!                                 bufp->dataVersion = -1;	/* known bad */
!                                 bufp->dirtyCounter++;
!                         }
! 			else {
! 				/* don't set dirty, since dirty implies
! 				 * currently up-to-date.  Don't need to do this,
! 				 * since we'll update the length anyway.
! 				 *
! 				 * Zero out remainder of the page, in case we
! 				 * seek and write past EOF, and make this data
! 				 * visible again.
!                                  */
!                                 bufferPos = sizep->LowPart & (buf_bufferSize - 1);
!                                 osi_assert(bufferPos != 0);
!                                 memset(bufp->datap + bufferPos, 0,
! 					buf_bufferSize - bufferPos);
! 			}
  
!                         lock_ReleaseWrite(&buf_globalLock);
!                 }
  		
!                 lock_ReleaseMutex(&scp->mx);
! 		lock_ReleaseMutex(&bufp->mx);
! 		if (!didRelease) {
! 			lock_ObtainWrite(&buf_globalLock);
! 			nbufp = bufp->fileHashp;
!                         if (nbufp) nbufp->refCount++;
!                         buf_LockedRelease(bufp);
! 			lock_ReleaseWrite(&buf_globalLock);
! 		}
! 
! 		/* bail out early if we fail */
!                 if (code) {
! 			/* at this point, nbufp is held; bufp has already been
!                          * released.
!                          */
!                         if (nbufp) buf_Release(nbufp);
!                 	return code;
! 		}
! 	}
! 	
!         /* success */
!         return 0;
  }
  
  long buf_FlushCleanPages(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
  {
! 	long code;
! 	cm_buf_t *bp;		/* buffer we're hacking on */
!         cm_buf_t *nbp;
!         int didRelease;
! 	long i;
! 
! 	i = BUF_FILEHASH(&scp->fid);
! 
! 	code = 0;
! 	lock_ObtainWrite(&buf_globalLock);
!         bp = buf_fileHashTablepp[i];
!         if (bp) bp->refCount++;
!         lock_ReleaseWrite(&buf_globalLock);
! 	for(; bp; bp = nbp) {
! 		didRelease = 0;	/* haven't released this buffer yet */
  
! 		/* clean buffer synchronously */
! 		if (cm_FidCmp(&bp->fid, &scp->fid) == 0) {
!                         lock_ObtainMutex(&bp->mx);
! 
! 			/* start cleaning the buffer, and wait for it to finish */
! 			buf_LockedCleanAsync(bp, reqp);
!                         buf_WaitIO(bp);
!                         lock_ReleaseMutex(&bp->mx);
! 
!                         code = (*cm_buf_opsp->Stabilizep)(scp, userp, reqp);
!                         if (code) goto skip;
! 
! 			lock_ObtainWrite(&buf_globalLock);
! 			/* actually, we only know that buffer is clean if ref
! 			 * count is 1, since we don't have buffer itself locked.
!                          */
! 			if (!(bp->flags & CM_BUF_DIRTY)) {
! 				if (bp->refCount == 1) {	/* bp is held above */
!                                 	buf_LockedRelease(bp);
!                                         nbp = bp->fileHashp;
!                                         if (nbp) nbp->refCount++;
!                                         didRelease = 1;
!                                 	buf_Recycle(bp);
! 				}
!                         }
! 			lock_ReleaseWrite(&buf_globalLock);
! 
!                         (*cm_buf_opsp->Unstabilizep)(scp, userp);
! 		}
! 
! skip:
! 		if (!didRelease) {
! 			lock_ObtainWrite(&buf_globalLock);
!                         if (nbp = bp->fileHashp) nbp->refCount++;
!                 	buf_LockedRelease(bp);
!                         lock_ReleaseWrite(&buf_globalLock);
! 		}
! 	}	/* for loop over a bunch of buffers */
! 	
!         /* done */
! 	return code;
! }
  
  long buf_CleanVnode(struct cm_scache *scp, cm_user_t *userp, cm_req_t *reqp)
  {
! 	long code;
! 	cm_buf_t *bp;		/* buffer we're hacking on */
      cm_buf_t *nbp;		/* next one */
! 	long i;
  
! 	i = BUF_FILEHASH(&scp->fid);
  
! 	code = 0;
! 	lock_ObtainWrite(&buf_globalLock);
      bp = buf_fileHashTablepp[i];
      if (bp) bp->refCount++;
      lock_ReleaseWrite(&buf_globalLock);
! 	for(; bp; bp = nbp) {
! 		/* clean buffer synchronously */
! 		if (cm_FidCmp(&bp->fid, &scp->fid) == 0) {
! 			if (userp) {
                  cm_HoldUser(userp);
! 				lock_ObtainMutex(&bp->mx);
! 				if (bp->userp) 
                      cm_ReleaseUser(bp->userp);
                  bp->userp = userp;
! 				lock_ReleaseMutex(&bp->mx);
!             }
! 			buf_CleanAsync(bp, reqp);
              buf_CleanWait(bp);
              lock_ObtainMutex(&bp->mx);
! 			if (bp->flags & CM_BUF_ERROR) {
! 				if (code == 0 || code == -1) code = bp->error;
!                 if (code == 0) code = -1;
              }
              lock_ReleaseMutex(&bp->mx);
! 		}
  
! 		lock_ObtainWrite(&buf_globalLock);
! 		buf_LockedRelease(bp);
          nbp = bp->fileHashp;
          if (nbp) nbp->refCount++;
! 		lock_ReleaseWrite(&buf_globalLock);
! 	}	/* for loop over a bunch of buffers */
! 	
      /* done */
! 	return code;
  }
  
  /* dump the contents of the buf_hashTablepp. */
--- 1225,1446 ----
   * Requires cm_bufCreateLock to be write locked.
   */
  long buf_Truncate(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
!                    osi_hyper_t *sizep)
  {
!     cm_buf_t *bufp;
!     cm_buf_t *nbufp;			/* next buffer, if didRelease */
!     osi_hyper_t bufEnd;
!     long code;
!     long bufferPos;
!     int didRelease;
!     long i;
! 
!     /* assert that cm_bufCreateLock is held in write mode */
!     lock_AssertWrite(&scp->bufCreateLock);
! 
!     i = BUF_FILEHASH(&scp->fid);
! 
!     lock_ObtainWrite(&buf_globalLock);
!     bufp = buf_fileHashTablepp[i];
!     if (bufp == NULL) {
!         lock_ReleaseWrite(&buf_globalLock);
!         return 0;
!     }
  
!     bufp->refCount++;
!     lock_ReleaseWrite(&buf_globalLock);
!     for(; bufp; bufp = nbufp) {
!         didRelease = 0;
!         lock_ObtainMutex(&bufp->mx);
! 
!         bufEnd.HighPart = 0;
!         bufEnd.LowPart = buf_bufferSize;
!         bufEnd = LargeIntegerAdd(bufEnd, bufp->offset);
! 
!         if (cm_FidCmp(&bufp->fid, &scp->fid) == 0 &&
!              LargeIntegerLessThan(*sizep, bufEnd)) {
!             buf_WaitIO(bufp);
!         }
!         lock_ObtainMutex(&scp->mx);
  	
!         /* make sure we have a callback (so we have the right value for
!          * the length), and wait for it to be safe to do a truncate.
!          */
!         code = cm_SyncOp(scp, bufp, userp, reqp, 0,
!                           CM_SCACHESYNC_NEEDCALLBACK
!                           | CM_SCACHESYNC_GETSTATUS
!                           | CM_SCACHESYNC_SETSIZE
!                           | CM_SCACHESYNC_BUFLOCKED);
!         /* if we succeeded in our locking, and this applies to the right
!          * file, and the truncate request overlaps the buffer either
!          * totally or partially, then do something.
!          */
!         if (code == 0 && cm_FidCmp(&bufp->fid, &scp->fid) == 0
!              && LargeIntegerLessThan(*sizep, bufEnd)) {
  
!             lock_ObtainWrite(&buf_globalLock);
  
!             /* destroy the buffer, turning off its dirty bit, if
!              * we're truncating the whole buffer.  Otherwise, set
!              * the dirty bit, and clear out the tail of the buffer
!              * if we just overlap some.
!              */
!             if (LargeIntegerLessThanOrEqualTo(*sizep, bufp->offset)) {
!                 /* truncating the entire page */
!                 bufp->flags &= ~CM_BUF_DIRTY;
!                 bufp->dataVersion = -1;	/* known bad */
!                 bufp->dirtyCounter++;
!             }
!             else {
!                 /* don't set dirty, since dirty implies
!                  * currently up-to-date.  Don't need to do this,
!                  * since we'll update the length anyway.
!                  *
!                  * Zero out remainder of the page, in case we
!                  * seek and write past EOF, and make this data
!                  * visible again.
!                  */
!                 bufferPos = sizep->LowPart & (buf_bufferSize - 1);
!                 osi_assert(bufferPos != 0);
!                 memset(bufp->datap + bufferPos, 0,
!                         buf_bufferSize - bufferPos);
!             }
! 
!             lock_ReleaseWrite(&buf_globalLock);
!         }
  		
!         lock_ReleaseMutex(&scp->mx);
!         lock_ReleaseMutex(&bufp->mx);
!         if (!didRelease) {
!             lock_ObtainWrite(&buf_globalLock);
!             nbufp = bufp->fileHashp;
!             if (nbufp) nbufp->refCount++;
!             buf_LockedRelease(bufp);
!             lock_ReleaseWrite(&buf_globalLock);
!         }
! 
!         /* bail out early if we fail */
!         if (code) {
!             /* at this point, nbufp is held; bufp has already been
!              * released.
!              */
!             if (nbufp) 
!                 buf_Release(nbufp);
!             return code;
!         }
!     }
! 
!     /* success */
!     return 0;
  }
  
  long buf_FlushCleanPages(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
  {
!     long code;
!     cm_buf_t *bp;		/* buffer we're hacking on */
!     cm_buf_t *nbp;
!     int didRelease;
!     long i;
  
!     i = BUF_FILEHASH(&scp->fid);
! 
!     code = 0;
!     lock_ObtainWrite(&buf_globalLock);
!     bp = buf_fileHashTablepp[i];
!     if (bp) bp->refCount++;
!     lock_ReleaseWrite(&buf_globalLock);
!     for(; bp; bp = nbp) {
!         didRelease = 0;	/* haven't released this buffer yet */
! 
!         /* clean buffer synchronously */
!         if (cm_FidCmp(&bp->fid, &scp->fid) == 0) {
!             lock_ObtainMutex(&bp->mx);
! 
!             /* start cleaning the buffer, and wait for it to finish */
!             buf_LockedCleanAsync(bp, reqp);
!             buf_WaitIO(bp);
!             lock_ReleaseMutex(&bp->mx);
! 
!             code = (*cm_buf_opsp->Stabilizep)(scp, userp, reqp);
!             if (code) goto skip;
! 
!             lock_ObtainWrite(&buf_globalLock);
!             /* actually, we only know that buffer is clean if ref
!              * count is 1, since we don't have buffer itself locked.
!              */
!             if (!(bp->flags & CM_BUF_DIRTY)) {
!                 if (bp->refCount == 1) {	/* bp is held above */
!                     buf_LockedRelease(bp);
!                     nbp = bp->fileHashp;
!                     if (nbp) nbp->refCount++;
!                     didRelease = 1;
!                     buf_Recycle(bp);
!                 }
!             }
!             lock_ReleaseWrite(&buf_globalLock);
! 
!             (*cm_buf_opsp->Unstabilizep)(scp, userp);
!         }
! 
!       skip:
!         if (!didRelease) {
!             lock_ObtainWrite(&buf_globalLock);
!             if (nbp = bp->fileHashp) nbp->refCount++;
!             buf_LockedRelease(bp);
!             lock_ReleaseWrite(&buf_globalLock);
!         }
!     }	/* for loop over a bunch of buffers */
! 
!     /* done */
!     return code;
! }       
  
  long buf_CleanVnode(struct cm_scache *scp, cm_user_t *userp, cm_req_t *reqp)
  {
!     long code;
!     cm_buf_t *bp;		/* buffer we're hacking on */
      cm_buf_t *nbp;		/* next one */
!     long i;
  
!     i = BUF_FILEHASH(&scp->fid);
  
!     code = 0;
!     lock_ObtainWrite(&buf_globalLock);
      bp = buf_fileHashTablepp[i];
      if (bp) bp->refCount++;
      lock_ReleaseWrite(&buf_globalLock);
!     for(; bp; bp = nbp) {
!         /* clean buffer synchronously */
!         if (cm_FidCmp(&bp->fid, &scp->fid) == 0) {
!             if (userp) {
                  cm_HoldUser(userp);
!                 lock_ObtainMutex(&bp->mx);
!                 if (bp->userp) 
                      cm_ReleaseUser(bp->userp);
                  bp->userp = userp;
!                 lock_ReleaseMutex(&bp->mx);
!             }   
!             buf_CleanAsync(bp, reqp);
              buf_CleanWait(bp);
              lock_ObtainMutex(&bp->mx);
!             if (bp->flags & CM_BUF_ERROR) {
!                 if (code == 0 || code == -1) 
!                     code = bp->error;
!                 if (code == 0) 
!                     code = -1;
              }
              lock_ReleaseMutex(&bp->mx);
!         }
  
!         lock_ObtainWrite(&buf_globalLock);
!         buf_LockedRelease(bp);
          nbp = bp->fileHashp;
          if (nbp) nbp->refCount++;
!         lock_ReleaseWrite(&buf_globalLock);
!     }	/* for loop over a bunch of buffers */
! 
      /* done */
!     return code;
  }
  
  /* dump the contents of the buf_hashTablepp. */
***************
*** 1439,1446 ****
      char output[1024];
      int i;
    
! 	if (buf_hashTablepp == NULL)
! 		return -1;
  
      lock_ObtainRead(&buf_globalLock);
    
--- 1451,1458 ----
      char output[1024];
      int i;
    
!     if (buf_hashTablepp == NULL)
!         return -1;
  
      lock_ObtainRead(&buf_globalLock);
    
Index: openafs/src/WINNT/afsd/cm_buf.h
diff -c openafs/src/WINNT/afsd/cm_buf.h:1.4.2.1 openafs/src/WINNT/afsd/cm_buf.h:1.4.2.2
*** openafs/src/WINNT/afsd/cm_buf.h:1.4.2.1	Tue Aug 17 00:28:38 2004
--- openafs/src/WINNT/afsd/cm_buf.h	Mon Oct 18 00:09:25 2004
***************
*** 72,78 ****
  				 */
          struct cm_buf *allp;	/* next in all list */
  	osi_mutex_t mx;		/* mutex protecting structure except refcount */
!     int refCount;		/* reference count (buf_globalLock) */
          long idCounter;		/* counter for softrefs; bumped at each recycle */
          long dirtyCounter;	/* bumped at each dirty->clean transition */
  #ifdef notdef
--- 72,78 ----
  				 */
          struct cm_buf *allp;	/* next in all list */
  	osi_mutex_t mx;		/* mutex protecting structure except refcount */
!     unsigned long refCount;		/* reference count (buf_globalLock) */
          long idCounter;		/* counter for softrefs; bumped at each recycle */
          long dirtyCounter;	/* bumped at each dirty->clean transition */
  #ifdef notdef
Index: openafs/src/WINNT/afsd/cm_callback.c
diff -c openafs/src/WINNT/afsd/cm_callback.c:1.20.2.3 openafs/src/WINNT/afsd/cm_callback.c:1.20.2.5
*** openafs/src/WINNT/afsd/cm_callback.c:1.20.2.3	Thu Aug 19 15:51:23 2004
--- openafs/src/WINNT/afsd/cm_callback.c	Mon Oct 18 00:09:25 2004
***************
*** 70,91 ****
   */
  void cm_RecordRacingRevoke(cm_fid_t *fidp, long cancelFlags)
  {
! 	cm_racingRevokes_t *rp;
  
! 	lock_ObtainWrite(&cm_callbackLock);
  
      osi_Log3(afsd_logp, "RecordRacingRevoke Volume %d Flags %lX activeCalls %d",
               fidp->volume, cancelFlags, cm_activeCallbackGrantingCalls);
  
! 	if (cm_activeCallbackGrantingCalls > 0) {
! 		rp = malloc(sizeof(*rp));
! 	        memset(rp, 0, sizeof(*rp));
! 	        osi_QAdd((osi_queue_t **) &cm_racingRevokesp, &rp->q);
!                 rp->flags |= (cancelFlags & CM_RACINGFLAG_ALL);
! 		if (fidp) rp->fid = *fidp;
!                 rp->callbackCount = ++cm_callbackCount;
! 	}
! 	lock_ReleaseWrite(&cm_callbackLock);
  }
  
  /*
--- 70,91 ----
   */
  void cm_RecordRacingRevoke(cm_fid_t *fidp, long cancelFlags)
  {
!     cm_racingRevokes_t *rp;
  
!     lock_ObtainWrite(&cm_callbackLock);
  
      osi_Log3(afsd_logp, "RecordRacingRevoke Volume %d Flags %lX activeCalls %d",
               fidp->volume, cancelFlags, cm_activeCallbackGrantingCalls);
  
!     if (cm_activeCallbackGrantingCalls > 0) {
!         rp = malloc(sizeof(*rp));
!         memset(rp, 0, sizeof(*rp));
!         osi_QAdd((osi_queue_t **) &cm_racingRevokesp, &rp->q);
!         rp->flags |= (cancelFlags & CM_RACINGFLAG_ALL);
!         if (fidp) rp->fid = *fidp;
!         rp->callbackCount = ++cm_callbackCount;
!     }
!     lock_ReleaseWrite(&cm_callbackLock);
  }
  
  /*
***************
*** 128,177 ****
   */
  void cm_RevokeCallback(struct rx_call *callp, AFSFid *fidp)
  {
! 	cm_fid_t tfid;
!         cm_scache_t *scp;
!         long hash;
          
! 	/* don't bother setting cell, since we won't be checking it (to aid
!          * in working with multi-homed servers: we don't know the cell if we
!          * don't recognize the IP address).
!          */
! 	tfid.cell = 0;
!         tfid.volume = fidp->Volume;
!         tfid.vnode = fidp->Vnode;
!         tfid.unique = fidp->Unique;
!         hash = CM_SCACHE_HASH(&tfid);
  
      osi_Log3(afsd_logp, "RevokeCallback vol %d vn %d un %d",
! 		 fidp->Volume, fidp->Vnode, fidp->Unique);
          
! 	/* do this first, so that if we're executing a callback granting call
!          * at this moment, we kill it before it can be merged in.  Otherwise,
!          * it could complete while we're doing the scan below, and get missed
!          * by both the scan and by this code.
!          */
! 	cm_RecordRacingRevoke(&tfid, 0);
  
! 	lock_ObtainWrite(&cm_scacheLock);
! 	/* do all in the hash bucket, since we don't know how many we'll find with
!          * varying cells.
!          */
!         for(scp = cm_hashTablep[hash]; scp; scp=scp->nextp) {
! 		if (scp->fid.volume == tfid.volume &&
!                 	scp->fid.vnode == tfid.vnode &&
!                         scp->fid.unique == tfid.unique) {
! 			scp->refCount++;
! 			lock_ReleaseWrite(&cm_scacheLock);
              osi_Log1(afsd_logp, "Discarding SCache scp %x", scp);
!                         lock_ObtainMutex(&scp->mx);
! 			cm_DiscardSCache(scp);
!                         lock_ReleaseMutex(&scp->mx);
! 			cm_CallbackNotifyChange(scp);
!                         lock_ObtainWrite(&cm_scacheLock);
!                         scp->refCount--;
! 		}
          }
! 	lock_ReleaseWrite(&cm_scacheLock);
  }
  
  /* called to revoke a volume callback, which is typically issued when a volume
--- 128,177 ----
   */
  void cm_RevokeCallback(struct rx_call *callp, AFSFid *fidp)
  {
!     cm_fid_t tfid;
!     cm_scache_t *scp;
!     long hash;
          
!     /* don't bother setting cell, since we won't be checking it (to aid
!      * in working with multi-homed servers: we don't know the cell if we
!      * don't recognize the IP address).
!      */
!     tfid.cell = 0;
!     tfid.volume = fidp->Volume;
!     tfid.vnode = fidp->Vnode;
!     tfid.unique = fidp->Unique;
!     hash = CM_SCACHE_HASH(&tfid);
  
      osi_Log3(afsd_logp, "RevokeCallback vol %d vn %d un %d",
!              fidp->Volume, fidp->Vnode, fidp->Unique);
          
!     /* do this first, so that if we're executing a callback granting call
!      * at this moment, we kill it before it can be merged in.  Otherwise,
!      * it could complete while we're doing the scan below, and get missed
!      * by both the scan and by this code.
!      */
!     cm_RecordRacingRevoke(&tfid, 0);
  
!     lock_ObtainWrite(&cm_scacheLock);
!     /* do all in the hash bucket, since we don't know how many we'll find with
!      * varying cells.
!      */
!     for (scp = cm_hashTablep[hash]; scp; scp=scp->nextp) {
!         if (scp->fid.volume == tfid.volume &&
!              scp->fid.vnode == tfid.vnode &&
!              scp->fid.unique == tfid.unique) {
!             scp->refCount++;
!             lock_ReleaseWrite(&cm_scacheLock);
              osi_Log1(afsd_logp, "Discarding SCache scp %x", scp);
!             lock_ObtainMutex(&scp->mx);
!             cm_DiscardSCache(scp);
!             lock_ReleaseMutex(&scp->mx);
!             cm_CallbackNotifyChange(scp);
!             lock_ObtainWrite(&cm_scacheLock);
!             scp->refCount--;
          }
!     }
!     lock_ReleaseWrite(&cm_scacheLock);
  }
  
  /* called to revoke a volume callback, which is typically issued when a volume
***************
*** 181,220 ****
   */
  void cm_RevokeVolumeCallback(struct rx_call *callp, AFSFid *fidp)
  {
! 	long hash;
!         cm_scache_t *scp;
!         cm_fid_t tfid;
  
      osi_Log1(afsd_logp, "RevokeVolumeCallback %d", fidp->Volume);
  
! 	/* do this first, so that if we're executing a callback granting call
!          * at this moment, we kill it before it can be merged in.  Otherwise,
!          * it could complete while we're doing the scan below, and get missed
!          * by both the scan and by this code.
!          */
! 	tfid.cell = tfid.vnode = tfid.unique = 0;
!         tfid.volume = fidp->Volume;
!         cm_RecordRacingRevoke(&tfid, CM_RACINGFLAG_CANCELVOL);
  
  
!         lock_ObtainWrite(&cm_scacheLock);
! 	for(hash = 0; hash < cm_hashTableSize; hash++) {
! 		for(scp=cm_hashTablep[hash]; scp; scp=scp->nextp) {
! 			if (scp->fid.volume == fidp->Volume) {
! 				scp->refCount++;
! 	                        lock_ReleaseWrite(&cm_scacheLock);
! 	                        lock_ObtainMutex(&scp->mx);
                  osi_Log1(afsd_logp, "Discarding SCache scp %x", scp);
! 				cm_DiscardSCache(scp);
! 				lock_ReleaseMutex(&scp->mx);
! 				cm_CallbackNotifyChange(scp);
! 	                        lock_ObtainWrite(&cm_scacheLock);
! 	                        scp->refCount--;
! 	                }
! 		}	/* search one hash bucket */
! 	}	/* search all hash buckets */
!         
!         lock_ReleaseWrite(&cm_scacheLock);
  }
  
  /* handle incoming RPC callback breaking message.
--- 181,220 ----
   */
  void cm_RevokeVolumeCallback(struct rx_call *callp, AFSFid *fidp)
  {
!     long hash;
!     cm_scache_t *scp;
!     cm_fid_t tfid;
  
      osi_Log1(afsd_logp, "RevokeVolumeCallback %d", fidp->Volume);
  
!     /* do this first, so that if we're executing a callback granting call
!      * at this moment, we kill it before it can be merged in.  Otherwise,
!      * it could complete while we're doing the scan below, and get missed
!      * by both the scan and by this code.
!      */
!     tfid.cell = tfid.vnode = tfid.unique = 0;
!     tfid.volume = fidp->Volume;
!     cm_RecordRacingRevoke(&tfid, CM_RACINGFLAG_CANCELVOL);
  
  
!     lock_ObtainWrite(&cm_scacheLock);
!     for (hash = 0; hash < cm_hashTableSize; hash++) {
!         for(scp=cm_hashTablep[hash]; scp; scp=scp->nextp) {
!             if (scp->fid.volume == fidp->Volume) {
!                 scp->refCount++;
!                 lock_ReleaseWrite(&cm_scacheLock);
!                 lock_ObtainMutex(&scp->mx);
                  osi_Log1(afsd_logp, "Discarding SCache scp %x", scp);
!                 cm_DiscardSCache(scp);
!                 lock_ReleaseMutex(&scp->mx);
!                 cm_CallbackNotifyChange(scp);
!                 lock_ObtainWrite(&cm_scacheLock);
!                 scp->refCount--;
!             }
!         }	/* search one hash bucket */
!     }	/* search all hash buckets */
! 
!     lock_ReleaseWrite(&cm_scacheLock);
  }
  
  /* handle incoming RPC callback breaking message.
***************
*** 222,244 ****
   */
  SRXAFSCB_CallBack(struct rx_call *callp, AFSCBFids *fidsArrayp, AFSCBs *cbsArrayp)
  {
!         int i;
!         AFSFid *tfidp;
          
      osi_Log0(afsd_logp, "SRXAFSCB_CallBack");
  
!         for(i=0; i < (long) fidsArrayp->AFSCBFids_len; i++) {
! 		tfidp = &fidsArrayp->AFSCBFids_val[i];
                  
          if (tfidp->Volume == 0)
              continue;   /* means don't do anything */
!                 else if (tfidp->Vnode == 0)
!                 	cm_RevokeVolumeCallback(callp, tfidp);
          else
              cm_RevokeCallback(callp, tfidp);
!         }
  
! 	return 0;
  }
  
  /* called with no locks by RPC system when a server indicates that it has never
--- 222,244 ----
   */
  SRXAFSCB_CallBack(struct rx_call *callp, AFSCBFids *fidsArrayp, AFSCBs *cbsArrayp)
  {
!     int i;
!     AFSFid *tfidp;
          
      osi_Log0(afsd_logp, "SRXAFSCB_CallBack");
  
!     for (i=0; i < (long) fidsArrayp->AFSCBFids_len; i++) {
!         tfidp = &fidsArrayp->AFSCBFids_val[i];
                  
          if (tfidp->Volume == 0)
              continue;   /* means don't do anything */
!         else if (tfidp->Vnode == 0)
!             cm_RevokeVolumeCallback(callp, tfidp);
          else
              cm_RevokeCallback(callp, tfidp);
!     }
  
!     return 0;
  }
  
  /* called with no locks by RPC system when a server indicates that it has never
***************
*** 288,319 ****
  	 * are "rare," hopefully this won't be a problem.
  	 */
  	lock_ObtainWrite(&cm_scacheLock);
! 	for(hash = 0; hash < cm_hashTableSize; hash++) {
! 		for(scp=cm_hashTablep[hash]; scp; scp=scp->nextp) {
! 			scp->refCount++;
!                         lock_ReleaseWrite(&cm_scacheLock);
!                         lock_ObtainMutex(&scp->mx);
! 			discarded = 0;
! 			if (scp->cbServerp != NULL) {
! 				/* we have a callback, now decide if we should clear it */
! 				if (scp->cbServerp == tsp || tsp == NULL) {
                          osi_Log1(afsd_logp, "Discarding SCache scp %x", scp);
! 					cm_DiscardSCache(scp);
! 					discarded = 1;
! 				}
! 			}
! 			lock_ReleaseMutex(&scp->mx);
! 			if (discarded)
! 				cm_CallbackNotifyChange(scp);
!                         lock_ObtainWrite(&cm_scacheLock);
!                         scp->refCount--;
! 		}	/* search one hash bucket */
! 	}	/* search all hash buckets */
  	
  	lock_ReleaseWrite(&cm_scacheLock);
  	
  	/* we're done with the server structure */
! 	if (tsp) cm_PutServer(tsp);
      }
  
      return 0;
--- 288,320 ----
  	 * are "rare," hopefully this won't be a problem.
  	 */
  	lock_ObtainWrite(&cm_scacheLock);
! 	for (hash = 0; hash < cm_hashTableSize; hash++) {
!             for (scp=cm_hashTablep[hash]; scp; scp=scp->nextp) {
!                 scp->refCount++;
!                 lock_ReleaseWrite(&cm_scacheLock);
!                 lock_ObtainMutex(&scp->mx);
!                 discarded = 0;
!                 if (scp->cbServerp != NULL) {
!                     /* we have a callback, now decide if we should clear it */
!                     if (scp->cbServerp == tsp || tsp == NULL) {
                          osi_Log1(afsd_logp, "Discarding SCache scp %x", scp);
!                         cm_DiscardSCache(scp);
!                         discarded = 1;
!                     }
!                 }
!                 lock_ReleaseMutex(&scp->mx);
!                 if (discarded)
!                     cm_CallbackNotifyChange(scp);
!                 lock_ObtainWrite(&cm_scacheLock);
!                 scp->refCount--;
!             }	/* search one hash bucket */
! 	}      	/* search all hash buckets */
  	
  	lock_ReleaseWrite(&cm_scacheLock);
  	
  	/* we're done with the server structure */
! 	if (tsp) 
!             cm_PutServer(tsp);
      }
  
      return 0;
***************
*** 323,329 ****
  SRXAFSCB_Probe(struct rx_call *callp)
  {
      osi_Log0(afsd_logp, "SRXAFSCB_Probe - not implemented");
! 	return 0;
  }
  
  /* debug interface: not implemented */
--- 324,330 ----
  SRXAFSCB_Probe(struct rx_call *callp)
  {
      osi_Log0(afsd_logp, "SRXAFSCB_Probe - not implemented");
!     return 0;
  }
  
  /* debug interface: not implemented */
***************
*** 337,403 ****
  /* debug interface: not implemented */
  SRXAFSCB_GetLock(struct rx_call *callp, long index, AFSDBLock *lockp)
  {
! 	/* XXXX */
      osi_Log0(afsd_logp, "SRXAFSCB_GetLock - not implemented");
! 	return RXGEN_OPCODE;
  }
  
  /* debug interface: not implemented */
  SRXAFSCB_GetCE(struct rx_call *callp, long index, AFSDBCacheEntry *cep)
  {
! 	/* XXXX */
      osi_Log0(afsd_logp, "SRXAFSCB_GetCE - not implemented");
! 	return RXGEN_OPCODE;
  }
  
  /* debug interface: not implemented */
  SRXAFSCB_XStatsVersion(struct rx_call *callp, long *vp)
  {
! 	/* XXXX */
      osi_Log0(afsd_logp, "SRXAFSCB_XStatsVersion - not implemented");
! 	*vp = -1;
! 	return RXGEN_OPCODE;
  }
  
  /* debug interface: not implemented */
  SRXAFSCB_GetXStats(struct rx_call *callp, long cvn, long coln, long *srvp, long *timep,
  	AFSCB_CollData *datap)
  {
! 	/* XXXX */
      osi_Log0(afsd_logp, "SRXAFSCB_GetXStats - not implemented");
! 	return RXGEN_OPCODE;
  }
  
  /* debug interface: not implemented */
  SRXAFSCB_InitCallBackState2(struct rx_call *callp, struct interfaceAddr* addr)
  {
! 	/* XXXX */
      osi_Log0(afsd_logp, "SRXAFSCB_InitCallBackState2 - not implemented");
! 	return RXGEN_OPCODE;
  }
  
  /* debug interface: not implemented */
  SRXAFSCB_WhoAreYou(struct rx_call *callp, struct interfaceAddr* addr)
  {
! 	/* XXXX */
      osi_Log0(afsd_logp, "SRXAFSCB_WhoAreYou - not implemented");
! 	return RXGEN_OPCODE;
  }
  
  /* debug interface: not implemented */
  SRXAFSCB_InitCallBackState3(struct rx_call *callp, afsUUID* serverUuid)
  {
! 	/* XXXX */
      osi_Log0(afsd_logp, "SRXAFSCB_InitCallBackState3 - not implemented");
! 	return RXGEN_OPCODE;
  }
  
  /* debug interface: not implemented */
  SRXAFSCB_ProbeUuid(struct rx_call *callp, afsUUID* clientUuid)
  {
! 	/* XXXX */
      osi_Log0(afsd_logp, "SRXAFSCB_ProbeUuid - not implemented");
! 	return RXGEN_OPCODE;
  }
  
  /*------------------------------------------------------------------------
--- 338,404 ----
  /* debug interface: not implemented */
  SRXAFSCB_GetLock(struct rx_call *callp, long index, AFSDBLock *lockp)
  {
!     /* XXXX */
      osi_Log0(afsd_logp, "SRXAFSCB_GetLock - not implemented");
!     return RXGEN_OPCODE;
  }
  
  /* debug interface: not implemented */
  SRXAFSCB_GetCE(struct rx_call *callp, long index, AFSDBCacheEntry *cep)
  {
!     /* XXXX */
      osi_Log0(afsd_logp, "SRXAFSCB_GetCE - not implemented");
!     return RXGEN_OPCODE;
  }
  
  /* debug interface: not implemented */
  SRXAFSCB_XStatsVersion(struct rx_call *callp, long *vp)
  {
!     /* XXXX */
      osi_Log0(afsd_logp, "SRXAFSCB_XStatsVersion - not implemented");
!     *vp = -1;
!     return RXGEN_OPCODE;
  }
  
  /* debug interface: not implemented */
  SRXAFSCB_GetXStats(struct rx_call *callp, long cvn, long coln, long *srvp, long *timep,
  	AFSCB_CollData *datap)
  {
!     /* XXXX */
      osi_Log0(afsd_logp, "SRXAFSCB_GetXStats - not implemented");
!     return RXGEN_OPCODE;
  }
  
  /* debug interface: not implemented */
  SRXAFSCB_InitCallBackState2(struct rx_call *callp, struct interfaceAddr* addr)
  {
!     /* XXXX */
      osi_Log0(afsd_logp, "SRXAFSCB_InitCallBackState2 - not implemented");
!     return RXGEN_OPCODE;
  }
  
  /* debug interface: not implemented */
  SRXAFSCB_WhoAreYou(struct rx_call *callp, struct interfaceAddr* addr)
  {
!     /* XXXX */
      osi_Log0(afsd_logp, "SRXAFSCB_WhoAreYou - not implemented");
!     return RXGEN_OPCODE;
  }
  
  /* debug interface: not implemented */
  SRXAFSCB_InitCallBackState3(struct rx_call *callp, afsUUID* serverUuid)
  {
!     /* XXXX */
      osi_Log0(afsd_logp, "SRXAFSCB_InitCallBackState3 - not implemented");
!     return RXGEN_OPCODE;
  }
  
  /* debug interface: not implemented */
  SRXAFSCB_ProbeUuid(struct rx_call *callp, afsUUID* clientUuid)
  {
!     /* XXXX */
      osi_Log0(afsd_logp, "SRXAFSCB_ProbeUuid - not implemented");
!     return RXGEN_OPCODE;
  }
  
  /*------------------------------------------------------------------------
***************
*** 608,615 ****
  /* called by afsd without any locks to initialize this module */
  void cm_InitCallback(void)
  {
! 	lock_InitializeRWLock(&cm_callbackLock, "cm_callbackLock");
!         cm_activeCallbackGrantingCalls = 0;
  }
  
  /* called with locked scp; tells us whether we've got a callback.
--- 609,616 ----
  /* called by afsd without any locks to initialize this module */
  void cm_InitCallback(void)
  {
!     lock_InitializeRWLock(&cm_callbackLock, "cm_callbackLock");
!     cm_activeCallbackGrantingCalls = 0;
  }
  
  /* called with locked scp; tells us whether we've got a callback.
***************
*** 633,639 ****
      // to be called because cm_GetCallback has some initialization work to do.
      // If cm_fakeDirCallback is 2, then it means that the fake directory is in
      // good shape and we simply return true, provided no change is detected.
!   int fdc, fgc;
  
      if (cm_freelanceEnabled && 
           scp->fid.cell==AFS_FAKE_ROOT_CELL_ID && scp->fid.volume==AFS_FAKE_ROOT_VOL_ID) {
--- 634,640 ----
      // to be called because cm_GetCallback has some initialization work to do.
      // If cm_fakeDirCallback is 2, then it means that the fake directory is in
      // good shape and we simply return true, provided no change is detected.
!     int fdc, fgc;
  
      if (cm_freelanceEnabled && 
           scp->fid.cell==AFS_FAKE_ROOT_CELL_ID && scp->fid.volume==AFS_FAKE_ROOT_VOL_ID) {
***************
*** 691,702 ****
   */
  void cm_StartCallbackGrantingCall(cm_scache_t *scp, cm_callbackRequest_t *cbrp)
  {
! 	lock_ObtainWrite(&cm_callbackLock);
! 	cbrp->callbackCount = cm_callbackCount;
!         cm_activeCallbackGrantingCalls++;
!         cbrp->startTime = osi_Time();
!         cbrp->serverp = NULL;
! 	lock_ReleaseWrite(&cm_callbackLock);
  }
  
  /* Called at the end of a callback-granting call, to remove the callback
--- 692,703 ----
   */
  void cm_StartCallbackGrantingCall(cm_scache_t *scp, cm_callbackRequest_t *cbrp)
  {
!     lock_ObtainWrite(&cm_callbackLock);
!     cbrp->callbackCount = cm_callbackCount;
!     cm_activeCallbackGrantingCalls++;
!     cbrp->startTime = osi_Time();
!     cbrp->serverp = NULL;
!     lock_ReleaseWrite(&cm_callbackLock);
  }
  
  /* Called at the end of a callback-granting call, to remove the callback
***************
*** 706,788 ****
   * this locking hierarchy.
   */
  void cm_EndCallbackGrantingCall(cm_scache_t *scp, cm_callbackRequest_t *cbrp,
! 	AFSCallBack *cbp, long flags)
  {
! 	cm_racingRevokes_t *revp;		/* where we are */
! 	cm_racingRevokes_t *nrevp;		/* where we'll be next */
!         int freeFlag;
      cm_server_t * serverp = 0;
  
! 	lock_ObtainWrite(&cm_callbackLock);
! 	if (flags & CM_CALLBACK_MAINTAINCOUNT) {
!         	osi_assert(cm_activeCallbackGrantingCalls > 0);
! 	}
! 	else {
! 		osi_assert(cm_activeCallbackGrantingCalls-- > 0);
! 	}
      if (cm_activeCallbackGrantingCalls == 0) 
          freeFlag = 1;
      else 
          freeFlag = 0;
  
! 	/* record the callback; we'll clear it below if we really lose it */
      if (cbrp) {
  	if (scp) {
              if (scp->cbServerp != cbrp->serverp) {
                  serverp = scp->cbServerp;
              }
! 	        scp->cbServerp = cbrp->serverp;
! 	        scp->cbExpires = cbrp->startTime + cbp->ExpirationTime;
          } else {
              serverp = cbrp->serverp;
          }
          cbrp->serverp = NULL;
! 	}
  
! 	/* a callback was actually revoked during our granting call, so
! 	 * run down the list of revoked fids, looking for ours.
! 	 * If activeCallbackGrantingCalls is zero, free the elements, too.
! 	 *
!          * May need to go through entire list just to do the freeing.
! 	 */
! 	for(revp = cm_racingRevokesp; revp; revp = nrevp) {
! 		nrevp = (cm_racingRevokes_t *) osi_QNext(&revp->q);
! 		/* if this callback came in later than when we started the
!                  * callback-granting call, and if this fid is the right fid,
!                  * then clear the callback.
!                  */
          if (scp && cbrp && cbrp->callbackCount != cm_callbackCount
!                        	&& revp->callbackCount > cbrp->callbackCount
               && (( scp->fid.volume == revp->fid.volume &&
!                                  scp->fid.vnode == revp->fid.vnode &&
!                                  scp->fid.unique == revp->fid.unique)
!                             ||
!                                 ((revp->flags & CM_RACINGFLAG_CANCELVOL) &&
!                                  scp->fid.volume == revp->fid.volume)
!                             ||
!                             	(revp->flags & CM_RACINGFLAG_CANCELALL))) {
! 			/* this one matches */
! 			osi_Log4(afsd_logp,
! 			"Racing revoke scp %x old cbc %d rev cbc %d cur cbc %d",
! 				 scp,
! 				 cbrp->callbackCount, revp->callbackCount,
! 				 cm_callbackCount);
! 			cm_DiscardSCache(scp);
! 			/*
! 			 * Since we don't have a callback to preserve, it's
! 			 * OK to drop the lock and re-obtain it.
! 			 */
! 			lock_ReleaseMutex(&scp->mx);
! 			cm_CallbackNotifyChange(scp);
! 			lock_ObtainMutex(&scp->mx);
!                 }
!                 if (freeFlag) free(revp);
          }
  
! 	/* if we freed the list, zap the pointer to it */
! 	if (freeFlag) cm_racingRevokesp = NULL;
  
! 	lock_ReleaseWrite(&cm_callbackLock);
  
      if ( serverp ) {
          lock_ObtainWrite(&cm_serverLock);
--- 707,789 ----
   * this locking hierarchy.
   */
  void cm_EndCallbackGrantingCall(cm_scache_t *scp, cm_callbackRequest_t *cbrp,
!                                 AFSCallBack *cbp, long flags)
  {
!     cm_racingRevokes_t *revp;		/* where we are */
!     cm_racingRevokes_t *nrevp;		/* where we'll be next */
!     int freeFlag;
      cm_server_t * serverp = 0;
  
!     lock_ObtainWrite(&cm_callbackLock);
!     if (flags & CM_CALLBACK_MAINTAINCOUNT) {
!         osi_assert(cm_activeCallbackGrantingCalls > 0);
!     }
!     else {
!         osi_assert(cm_activeCallbackGrantingCalls-- > 0);
!     }
      if (cm_activeCallbackGrantingCalls == 0) 
          freeFlag = 1;
      else 
          freeFlag = 0;
  
!     /* record the callback; we'll clear it below if we really lose it */
      if (cbrp) {
  	if (scp) {
              if (scp->cbServerp != cbrp->serverp) {
                  serverp = scp->cbServerp;
              }
!             scp->cbServerp = cbrp->serverp;
!             scp->cbExpires = cbrp->startTime + cbp->ExpirationTime;
          } else {
              serverp = cbrp->serverp;
          }
          cbrp->serverp = NULL;
!     }
  
!     /* a callback was actually revoked during our granting call, so
!      * run down the list of revoked fids, looking for ours.
!      * If activeCallbackGrantingCalls is zero, free the elements, too.
!      *
!      * May need to go through entire list just to do the freeing.
!      */
!     for (revp = cm_racingRevokesp; revp; revp = nrevp) {
!         nrevp = (cm_racingRevokes_t *) osi_QNext(&revp->q);
!         /* if this callback came in later than when we started the
!          * callback-granting call, and if this fid is the right fid,
!          * then clear the callback.
!          */
          if (scp && cbrp && cbrp->callbackCount != cm_callbackCount
!              && revp->callbackCount > cbrp->callbackCount
               && (( scp->fid.volume == revp->fid.volume &&
!                    scp->fid.vnode == revp->fid.vnode &&
!                    scp->fid.unique == revp->fid.unique)
!                   ||
!                   ((revp->flags & CM_RACINGFLAG_CANCELVOL) &&
!                     scp->fid.volume == revp->fid.volume)
!                   ||
!                   (revp->flags & CM_RACINGFLAG_CANCELALL))) {
!             /* this one matches */
!             osi_Log4(afsd_logp,
!                       "Racing revoke scp %x old cbc %d rev cbc %d cur cbc %d",
!                       scp,
!                       cbrp->callbackCount, revp->callbackCount,
!                       cm_callbackCount);
!             cm_DiscardSCache(scp);
!             /*
!              * Since we don't have a callback to preserve, it's
!              * OK to drop the lock and re-obtain it.
!              */
!             lock_ReleaseMutex(&scp->mx);
!             cm_CallbackNotifyChange(scp);
!             lock_ObtainMutex(&scp->mx);
          }
+         if (freeFlag) free(revp);
+     }
  
!     /* if we freed the list, zap the pointer to it */
!     if (freeFlag) cm_racingRevokesp = NULL;
  
!     lock_ReleaseWrite(&cm_callbackLock);
  
      if ( serverp ) {
          lock_ObtainWrite(&cm_serverLock);
***************
*** 795,803 ****
   * called with locked scp; returns with same.
   */
  long cm_GetCallback(cm_scache_t *scp, struct cm_user *userp,
! 	struct cm_req *reqp, long flags)
  {
! 	long code;
      cm_conn_t *connp;
      AFSFetchStatus afsStatus;
      AFSVolSync volSync;
--- 796,804 ----
   * called with locked scp; returns with same.
   */
  long cm_GetCallback(cm_scache_t *scp, struct cm_user *userp,
!                     struct cm_req *reqp, long flags)
  {
!     long code;
      cm_conn_t *connp;
      AFSFetchStatus afsStatus;
      AFSVolSync volSync;
***************
*** 807,820 ****
      int mustCall;
      long sflags;
      cm_fid_t sfid;
  
      osi_Log2(afsd_logp, "GetCallback scp %x flags %lX", scp, flags);
  
  #ifdef AFS_FREELANCE_CLIENT
! 	// The case where a callback is needed on /afs is handled
! 	// specially. We need to fetch the status by calling
! 	// cm_MergeStatus and mark that cm_fakeDirCallback is 2
! 	if (cm_freelanceEnabled) {
          if (scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
               scp->fid.volume==AFS_FAKE_ROOT_VOL_ID &&
               scp->fid.unique==0x1 &&
--- 808,822 ----
      int mustCall;
      long sflags;
      cm_fid_t sfid;
+     struct rx_connection * callp;
  
      osi_Log2(afsd_logp, "GetCallback scp %x flags %lX", scp, flags);
  
  #ifdef AFS_FREELANCE_CLIENT
!     // The case where a callback is needed on /afs is handled
!     // specially. We need to fetch the status by calling
!     // cm_MergeStatus and mark that cm_fakeDirCallback is 2
!     if (cm_freelanceEnabled) {
          if (scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
               scp->fid.volume==AFS_FAKE_ROOT_VOL_ID &&
               scp->fid.unique==0x1 &&
***************
*** 849,889 ****
      }
  #endif /* AFS_FREELANCE_CLIENT */
  	
! 	mustCall = (flags & 1);
! 	cm_AFSFidFromFid(&tfid, &scp->fid);
! 	while (1) {
! 		if (!mustCall && cm_HaveCallback(scp)) return 0;
  
          /* turn off mustCall, since it has now forced us past the check above */
          mustCall = 0;
  
          /* otherwise, we have to make an RPC to get the status */
! 		sflags = CM_SCACHESYNC_FETCHSTATUS | CM_SCACHESYNC_GETCALLBACK;
          cm_SyncOp(scp, NULL, NULL, NULL, 0, sflags);
          cm_StartCallbackGrantingCall(scp, &cbr);
          sfid = scp->fid;
! 		lock_ReleaseMutex(&scp->mx);
  		
! 		/* now make the RPC */
! 		osi_Log1(afsd_logp, "CALL FetchStatus vp %x", (long) scp);
          do {
! 			code = cm_Conn(&sfid, userp, reqp, &connp);
              if (code) continue;
! 		
!             code = RXAFS_FetchStatus(connp->callp, &tfid,
                                       &afsStatus, &callback, &volSync);
  
! 		} while (cm_Analyze(connp, userp, reqp, &sfid, &volSync, NULL,
                              &cbr, code));
          code = cm_MapRPCError(code, reqp);
! 		osi_Log0(afsd_logp, "CALL FetchStatus DONE");
  
! 		lock_ObtainMutex(&scp->mx);
          cm_SyncOpDone(scp, NULL, sflags);
! 		if (code == 0) {
              cm_EndCallbackGrantingCall(scp, &cbr, &callback, 0);
              cm_MergeStatus(scp, &afsStatus, &volSync, userp, 0);
! 		}   
          else
              cm_EndCallbackGrantingCall(NULL, &cbr, NULL, 0);
  
--- 851,893 ----
      }
  #endif /* AFS_FREELANCE_CLIENT */
  	
!     mustCall = (flags & 1);
!     cm_AFSFidFromFid(&tfid, &scp->fid);
!     while (1) {
!         if (!mustCall && cm_HaveCallback(scp)) return 0;
  
          /* turn off mustCall, since it has now forced us past the check above */
          mustCall = 0;
  
          /* otherwise, we have to make an RPC to get the status */
!         sflags = CM_SCACHESYNC_FETCHSTATUS | CM_SCACHESYNC_GETCALLBACK;
          cm_SyncOp(scp, NULL, NULL, NULL, 0, sflags);
          cm_StartCallbackGrantingCall(scp, &cbr);
          sfid = scp->fid;
!         lock_ReleaseMutex(&scp->mx);
  		
!         /* now make the RPC */
!         osi_Log1(afsd_logp, "CALL FetchStatus vp %x", (long) scp);
          do {
!             code = cm_Conn(&sfid, userp, reqp, &connp);
              if (code) continue;
! 
!             callp = cm_GetRxConn(connp);
!             code = RXAFS_FetchStatus(callp, &tfid,
                                       &afsStatus, &callback, &volSync);
+             rx_PutConnection(callp);
  
!         } while (cm_Analyze(connp, userp, reqp, &sfid, &volSync, NULL,
                              &cbr, code));
          code = cm_MapRPCError(code, reqp);
!         osi_Log0(afsd_logp, "CALL FetchStatus DONE");
  
!         lock_ObtainMutex(&scp->mx);
          cm_SyncOpDone(scp, NULL, sflags);
!         if (code == 0) {
              cm_EndCallbackGrantingCall(scp, &cbr, &callback, 0);
              cm_MergeStatus(scp, &afsStatus, &volSync, userp, 0);
!         }   
          else
              cm_EndCallbackGrantingCall(NULL, &cbr, NULL, 0);
  
***************
*** 903,910 ****
  
      now = osi_Time();
      lock_ObtainWrite(&cm_scacheLock);
!     for(i=0; i<cm_hashTableSize; i++) {
!         for(scp = cm_hashTablep[i]; scp; scp=scp->nextp) {
              scp->refCount++;
              lock_ReleaseWrite(&cm_scacheLock);
              if (scp->cbExpires > 0 && (scp->cbServerp == NULL || now > scp->cbExpires)) {
--- 907,914 ----
  
      now = osi_Time();
      lock_ObtainWrite(&cm_scacheLock);
!     for (i=0; i<cm_hashTableSize; i++) {
!         for (scp = cm_hashTablep[i]; scp; scp=scp->nextp) {
              scp->refCount++;
              lock_ReleaseWrite(&cm_scacheLock);
              if (scp->cbExpires > 0 && (scp->cbServerp == NULL || now > scp->cbExpires)) {
Index: openafs/src/WINNT/afsd/cm_conn.c
diff -c openafs/src/WINNT/afsd/cm_conn.c:1.25.2.1 openafs/src/WINNT/afsd/cm_conn.c:1.25.2.3
*** openafs/src/WINNT/afsd/cm_conn.c:1.25.2.1	Tue Aug 17 00:28:38 2004
--- openafs/src/WINNT/afsd/cm_conn.c	Mon Oct 18 00:09:25 2004
***************
*** 463,491 ****
  /* called with a held server to GC all bad connections hanging off of the server */
  void cm_GCConnections(cm_server_t *serverp)
  {
! 	cm_conn_t *tcp;
      cm_conn_t **lcpp;
      cm_user_t *userp;
  
! 	lock_ObtainWrite(&cm_connLock);
! 	lcpp = &serverp->connsp;
! 	for(tcp = *lcpp; tcp; tcp = *lcpp) {
! 		userp = tcp->userp;
! 		if (userp && tcp->refCount == 0 && (userp->vcRefs == 0)) {
! 			/* do the deletion of this guy */
              cm_PutServer(tcp->serverp);
              cm_ReleaseUser(userp);
              *lcpp = tcp->nextp;
! 			rx_DestroyConnection(tcp->callp);
              lock_FinalizeMutex(&tcp->mx);
              free(tcp);
          }
          else {
! 			/* just advance to the next */
              lcpp = &tcp->nextp;
          }
      }
! 	lock_ReleaseWrite(&cm_connLock);
  }
  
  static void cm_NewRXConnection(cm_conn_t *tcp, cm_ucell_t *ucellp,
--- 463,491 ----
  /* called with a held server to GC all bad connections hanging off of the server */
  void cm_GCConnections(cm_server_t *serverp)
  {
!     cm_conn_t *tcp;
      cm_conn_t **lcpp;
      cm_user_t *userp;
  
!     lock_ObtainWrite(&cm_connLock);
!     lcpp = &serverp->connsp;
!     for (tcp = *lcpp; tcp; tcp = *lcpp) {
!         userp = tcp->userp;
!         if (userp && tcp->refCount == 0 && (userp->vcRefs == 0)) {
!             /* do the deletion of this guy */
              cm_PutServer(tcp->serverp);
              cm_ReleaseUser(userp);
              *lcpp = tcp->nextp;
!             rx_DestroyConnection(tcp->callp);
              lock_FinalizeMutex(&tcp->mx);
              free(tcp);
          }
          else {
!             /* just advance to the next */
              lcpp = &tcp->nextp;
          }
      }
!     lock_ReleaseWrite(&cm_connLock);
  }
  
  static void cm_NewRXConnection(cm_conn_t *tcp, cm_ucell_t *ucellp,
***************
*** 495,519 ****
      int serviceID;
      int secIndex;
      struct rx_securityClass *secObjp;
! 	afs_int32 level;
  
! 	if (serverp->type == CM_SERVER_VLDB) {
! 		port = htons(7003);
          serviceID = 52;
      }
      else {
! 		osi_assert(serverp->type == CM_SERVER_FILE);
          port = htons(7000);
          serviceID = 1;
      }
! 	if (ucellp->flags & CM_UCELLFLAG_RXKAD) {
! 		secIndex = 2;
! 		if (cryptall) {
! 			level = rxkad_crypt;
! 			tcp->cryptlevel = rxkad_crypt;
! 		} else {
! 			level = rxkad_clear;
! 		}
          secObjp = rxkad_NewClientSecurityObject(level,
                                                  &ucellp->sessionKey, ucellp->kvno,
                                                  ucellp->ticketLen, ucellp->ticketp);    
--- 495,518 ----
      int serviceID;
      int secIndex;
      struct rx_securityClass *secObjp;
!     afs_int32 level;
  
!     if (serverp->type == CM_SERVER_VLDB) {
!         port = htons(7003);
          serviceID = 52;
      }
      else {
!         osi_assert(serverp->type == CM_SERVER_FILE);
          port = htons(7000);
          serviceID = 1;
      }
!     if (ucellp->flags & CM_UCELLFLAG_RXKAD) {
!         secIndex = 2;
!         if (cryptall) {
!             level = tcp->cryptlevel = rxkad_crypt;
!         } else {
!             level = tcp->cryptlevel = rxkad_clear;
!         }
          secObjp = rxkad_NewClientSecurityObject(level,
                                                  &ucellp->sessionKey, ucellp->kvno,
                                                  ucellp->ticketLen, ucellp->ticketp);    
***************
*** 523,583 ****
          secIndex = 0;
          secObjp = rxnull_NewClientSecurityObject();
      }
! 	osi_assert(secObjp != NULL);
      tcp->callp = rx_NewConnection(serverp->addr.sin_addr.s_addr,
                                    port,
                                    serviceID,
                                    secObjp,
                                    secIndex);
! 	rx_SetConnDeadTime(tcp->callp, ConnDeadtimeout);
! 	rx_SetConnHardDeadTime(tcp->callp, HardDeadtimeout);
! 	tcp->ucgen = ucellp->gen;
      if (secObjp)
          rxs_Release(secObjp);   /* Decrement the initial refCount */
  }
  
  long cm_ConnByServer(cm_server_t *serverp, cm_user_t *userp, cm_conn_t **connpp)
  {
! 	cm_conn_t *tcp;
      cm_ucell_t *ucellp;
  
! 	lock_ObtainMutex(&userp->mx);
! 	lock_ObtainWrite(&cm_connLock);
! 	for(tcp = serverp->connsp; tcp; tcp=tcp->nextp) {
          if (tcp->userp == userp) 
              break;
      }
      
! 	/* find ucell structure */
      ucellp = cm_GetUCell(userp, serverp->cellp);
! 	if (!tcp) {
          cm_GetServer(serverp);
! 		tcp = malloc(sizeof(*tcp));
          memset(tcp, 0, sizeof(*tcp));
          tcp->nextp = serverp->connsp;
          serverp->connsp = tcp;
          cm_HoldUser(userp);
          tcp->userp = userp;
          lock_InitializeMutex(&tcp->mx, "cm_conn_t mutex");
          tcp->serverp = serverp;
! 		tcp->cryptlevel = rxkad_clear;
! 		cm_NewRXConnection(tcp, ucellp, serverp);
! 		tcp->refCount = 1;
!     }
! 	else {
! 		if ((tcp->ucgen < ucellp->gen) || (tcp->cryptlevel != cryptall))
! 		{
! 			rx_DestroyConnection(tcp->callp);
! 			cm_NewRXConnection(tcp, ucellp, serverp);
! 		}
          tcp->refCount++;
! 	}
! 	lock_ReleaseWrite(&cm_connLock);
      lock_ReleaseMutex(&userp->mx);
  
! 	/* return this pointer to our caller */
      osi_Log1(afsd_logp, "cm_ConnByServer returning conn 0x%x", (long) tcp);
! 	*connpp = tcp;
  
      return 0;
  }
--- 522,586 ----
          secIndex = 0;
          secObjp = rxnull_NewClientSecurityObject();
      }
!     osi_assert(secObjp != NULL);
      tcp->callp = rx_NewConnection(serverp->addr.sin_addr.s_addr,
                                    port,
                                    serviceID,
                                    secObjp,
                                    secIndex);
!     rx_SetConnDeadTime(tcp->callp, ConnDeadtimeout);
!     rx_SetConnHardDeadTime(tcp->callp, HardDeadtimeout);
!     tcp->ucgen = ucellp->gen;
      if (secObjp)
          rxs_Release(secObjp);   /* Decrement the initial refCount */
  }
  
  long cm_ConnByServer(cm_server_t *serverp, cm_user_t *userp, cm_conn_t **connpp)
  {
!     cm_conn_t *tcp;
      cm_ucell_t *ucellp;
  
!     lock_ObtainMutex(&userp->mx);
!     lock_ObtainWrite(&cm_connLock);
!     for (tcp = serverp->connsp; tcp; tcp=tcp->nextp) {
          if (tcp->userp == userp) 
              break;
      }
      
!     /* find ucell structure */
      ucellp = cm_GetUCell(userp, serverp->cellp);
!     if (!tcp) {
          cm_GetServer(serverp);
!         tcp = malloc(sizeof(*tcp));
          memset(tcp, 0, sizeof(*tcp));
          tcp->nextp = serverp->connsp;
          serverp->connsp = tcp;
          cm_HoldUser(userp);
          tcp->userp = userp;
          lock_InitializeMutex(&tcp->mx, "cm_conn_t mutex");
+         lock_ObtainMutex(&tcp->mx);
          tcp->serverp = serverp;
!         tcp->cryptlevel = rxkad_clear;
!         cm_NewRXConnection(tcp, ucellp, serverp);
!         tcp->refCount = 1;
!         lock_ReleaseMutex(&tcp->mx);
!     } else {
!         if ((tcp->ucgen < ucellp->gen) ||
!             (tcp->cryptlevel != (cryptall ? rxkad_crypt : rxkad_clear)))
!         {
!             lock_ObtainMutex(&tcp->mx);
!             rx_DestroyConnection(tcp->callp);
!             cm_NewRXConnection(tcp, ucellp, serverp);
!             lock_ReleaseMutex(&tcp->mx);
!         }
          tcp->refCount++;
!     }
!     lock_ReleaseWrite(&cm_connLock);
      lock_ReleaseMutex(&userp->mx);
  
!     /* return this pointer to our caller */
      osi_Log1(afsd_logp, "cm_ConnByServer returning conn 0x%x", (long) tcp);
!     *connpp = tcp;
  
      return 0;
  }
***************
*** 599,601 ****
--- 602,616 ----
      cm_FreeServerList(serverspp);
      return code;
  }
+ 
+ extern struct rx_connection * 
+ cm_GetRxConn(cm_conn_t *connp)
+ {
+     struct rx_connection * rxconn;
+     lock_ObtainMutex(&connp->mx);
+     rxconn = connp->callp;
+     rx_GetConnection(rxconn);
+     lock_ReleaseMutex(&connp->mx);
+     return rxconn;
+ }
+ 
Index: openafs/src/WINNT/afsd/cm_conn.h
diff -c openafs/src/WINNT/afsd/cm_conn.h:1.8 openafs/src/WINNT/afsd/cm_conn.h:1.8.2.1
*** openafs/src/WINNT/afsd/cm_conn.h:1.8	Sat Jul 31 20:16:37 2004
--- openafs/src/WINNT/afsd/cm_conn.h	Mon Oct 18 00:09:26 2004
***************
*** 23,29 ****
          struct rx_connection *callp;	/* locked by mx */
          struct cm_user *userp;		/* locked by mx; a held reference */
          osi_mutex_t mx;			/* mutex for some of these fields */
!         int refCount;			/* locked by cm_connLock */
  	int ucgen;			/* ucellp's generation number */
          long flags;			/* locked by mx */
  	int cryptlevel;			/* encrytion status */
--- 23,29 ----
          struct rx_connection *callp;	/* locked by mx */
          struct cm_user *userp;		/* locked by mx; a held reference */
          osi_mutex_t mx;			/* mutex for some of these fields */
!         unsigned long refCount;			/* locked by cm_connLock */
  	int ucgen;			/* ucellp's generation number */
          long flags;			/* locked by mx */
  	int cryptlevel;			/* encrytion status */
***************
*** 83,88 ****
--- 83,89 ----
  				   cache managers treat it as "server is down"*/
  
  #include "cm_server.h"
+ #include "rx.h"
  
  extern void cm_InitConn(void);
  
***************
*** 106,109 ****
--- 107,112 ----
  
  extern void cm_GCConnections(cm_server_t *serverp);
  
+ extern struct rx_connection * cm_GetRxConn(cm_conn_t *connp);
+ 
  #endif /*  __CM_CONN_H_ENV__ */
Index: openafs/src/WINNT/afsd/cm_dcache.c
diff -c openafs/src/WINNT/afsd/cm_dcache.c:1.11.2.1 openafs/src/WINNT/afsd/cm_dcache.c:1.11.2.3
*** openafs/src/WINNT/afsd/cm_dcache.c:1.11.2.1	Tue Aug 17 00:28:39 2004
--- openafs/src/WINNT/afsd/cm_dcache.c	Mon Oct 18 00:09:26 2004
***************
*** 37,52 ****
   * or when holding or releasing a vnode pointer.
   */
  long cm_BufWrite(void *vfidp, osi_hyper_t *offsetp, long length, long flags,
! 	cm_user_t *userp, cm_req_t *reqp)
  {
! 	/* store the data back from this buffer; the buffer is locked and held,
! 	 * but the vnode involved isn't locked, yet.  It is held by its
! 	 * reference from the buffer, which won't change until the buffer is
! 	 * released by our caller.  Thus, we don't have to worry about holding
! 	 * bufp->scp.
!          */
! 	long code;
! 	cm_fid_t *fidp = vfidp;
      cm_scache_t *scp;
      long nbytes;
      long temp;
--- 37,52 ----
   * or when holding or releasing a vnode pointer.
   */
  long cm_BufWrite(void *vfidp, osi_hyper_t *offsetp, long length, long flags,
!                  cm_user_t *userp, cm_req_t *reqp)
  {
!     /* store the data back from this buffer; the buffer is locked and held,
!      * but the vnode involved isn't locked, yet.  It is held by its
!      * reference from the buffer, which won't change until the buffer is
!      * released by our caller.  Thus, we don't have to worry about holding
!      * bufp->scp.
!      */
!     long code;
!     cm_fid_t *fidp = vfidp;
      cm_scache_t *scp;
      long nbytes;
      long temp;
***************
*** 70,104 ****
       * drops lots of locks, and may indeed return a properly initialized
       * buffer, although more likely it will just return a new, empty, buffer.
       */
! 	scp = cm_FindSCache(fidp);
! 	if (scp == NULL)
! 		return CM_ERROR_NOSUCHFILE;	/* shouldn't happen */
  
! 	cm_AFSFidFromFid(&tfid, fidp);
  
! 	lock_ObtainMutex(&scp->mx);
          
      code = cm_SetupStoreBIOD(scp, offsetp, length, &biod, userp, reqp);
      if (code) {
! 		osi_Log1(afsd_logp, "cm_SetupStoreBIOD code %x", code);
! 		lock_ReleaseMutex(&scp->mx);
! 		cm_ReleaseSCache(scp);
          return code;
      }
  
! 	if (biod.length == 0) {
! 		osi_Log0(afsd_logp, "cm_SetupStoreBIOD length 0");
! 		lock_ReleaseMutex(&scp->mx);
! 		cm_ReleaseBIOD(&biod, 1);	/* should be a NOOP */
! 		cm_ReleaseSCache(scp);
          return 0;
! 	}   
  
! 	/* Serialize StoreData RPC's; for rationale see cm_scache.c */
! 	(void) cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA_EXCL);
  
! 	/* prepare the output status for the store */
! 	scp->mask |= CM_SCACHEMASK_CLIENTMODTIME;
      cm_StatusFromAttr(&inStatus, scp, NULL);
      truncPos = scp->length.LowPart;
      if ((scp->mask & CM_SCACHEMASK_TRUNCPOS)
--- 70,104 ----
       * drops lots of locks, and may indeed return a properly initialized
       * buffer, although more likely it will just return a new, empty, buffer.
       */
!     scp = cm_FindSCache(fidp);
!     if (scp == NULL)
!         return CM_ERROR_NOSUCHFILE;	/* shouldn't happen */
  
!     cm_AFSFidFromFid(&tfid, fidp);
  
!     lock_ObtainMutex(&scp->mx);
          
      code = cm_SetupStoreBIOD(scp, offsetp, length, &biod, userp, reqp);
      if (code) {
!         osi_Log1(afsd_logp, "cm_SetupStoreBIOD code %x", code);
!         lock_ReleaseMutex(&scp->mx);
!         cm_ReleaseSCache(scp);
          return code;
      }
  
!     if (biod.length == 0) {
!         osi_Log0(afsd_logp, "cm_SetupStoreBIOD length 0");
!         lock_ReleaseMutex(&scp->mx);
!         cm_ReleaseBIOD(&biod, 1);	/* should be a NOOP */
!         cm_ReleaseSCache(scp);
          return 0;
!     }   
  
!     /* Serialize StoreData RPC's; for rationale see cm_scache.c */
!     (void) cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA_EXCL);
  
!     /* prepare the output status for the store */
!     scp->mask |= CM_SCACHEMASK_CLIENTMODTIME;
      cm_StatusFromAttr(&inStatus, scp, NULL);
      truncPos = scp->length.LowPart;
      if ((scp->mask & CM_SCACHEMASK_TRUNCPOS)
***************
*** 106,151 ****
          truncPos = scp->truncPos.LowPart;
  	scp->mask &= ~CM_SCACHEMASK_TRUNCPOS;
                  
! 	/* compute how many bytes to write from this buffer */
      thyper = LargeIntegerSubtract(scp->length, biod.offset);
      if (LargeIntegerLessThanZero(thyper)) {
! 		/* entire buffer is past EOF */
! 		nbytes = 0;
      }
      else {
! 		/* otherwise write out part of buffer before EOF, but not
           * more than bufferSize bytes.
           */
! 		nbytes = thyper.LowPart;
          if (nbytes > biod.length) 
              nbytes = biod.length;
      }
  
! 	lock_ReleaseMutex(&scp->mx);
          
      /* now we're ready to do the store operation */
      do {
! 		code = cm_Conn(&scp->fid, userp, reqp, &connp);
          if (code) 
              continue;
  		
! 		callp = rx_NewCall(connp->callp);
  
! 		osi_Log3(afsd_logp, "CALL StoreData vp %x, off 0x%x, size 0x%x",
                   (long) scp, biod.offset.LowPart, nbytes);
  
          code = StartRXAFS_StoreData(callp, &tfid, &inStatus,
                                      biod.offset.LowPart, nbytes, truncPos);
  
! 		if (code == 0) {
              /* write the data from the the list of buffers */
              qdp = NULL;
! 			while(nbytes > 0) {
! 				if (qdp == NULL)
! 		 			qdp = biod.bufListEndp;
! 				else
! 					qdp = (osi_queueData_t *) osi_QPrev(&qdp->q);
! 				osi_assert(qdp != NULL);
                  bufp = osi_GetQData(qdp);
                  bufferp = bufp->datap;
                  wbytes = nbytes;
--- 106,153 ----
          truncPos = scp->truncPos.LowPart;
  	scp->mask &= ~CM_SCACHEMASK_TRUNCPOS;
                  
!     /* compute how many bytes to write from this buffer */
      thyper = LargeIntegerSubtract(scp->length, biod.offset);
      if (LargeIntegerLessThanZero(thyper)) {
!         /* entire buffer is past EOF */
!         nbytes = 0;
      }
      else {
!         /* otherwise write out part of buffer before EOF, but not
           * more than bufferSize bytes.
           */
!         nbytes = thyper.LowPart;
          if (nbytes > biod.length) 
              nbytes = biod.length;
      }
  
!     lock_ReleaseMutex(&scp->mx);
          
      /* now we're ready to do the store operation */
      do {
!         code = cm_Conn(&scp->fid, userp, reqp, &connp);
          if (code) 
              continue;
  		
!         lock_ObtainMutex(&connp->mx);
!         callp = rx_NewCall(connp->callp);
!         lock_ReleaseMutex(&connp->mx);
  
!         osi_Log3(afsd_logp, "CALL StoreData vp %x, off 0x%x, size 0x%x",
                   (long) scp, biod.offset.LowPart, nbytes);
  
          code = StartRXAFS_StoreData(callp, &tfid, &inStatus,
                                      biod.offset.LowPart, nbytes, truncPos);
  
!         if (code == 0) {
              /* write the data from the the list of buffers */
              qdp = NULL;
!             while(nbytes > 0) {
!                 if (qdp == NULL)
!                     qdp = biod.bufListEndp;
!                 else
!                     qdp = (osi_queueData_t *) osi_QPrev(&qdp->q);
!                 osi_assert(qdp != NULL);
                  bufp = osi_GetQData(qdp);
                  bufferp = bufp->datap;
                  wbytes = nbytes;
***************
*** 157,219 ****
                  if (temp != wbytes) {
                      osi_Log2(afsd_logp, "rx_Write failed %d != %d",temp,wbytes);
                      code = -1;
! 					break;
! 				} else {
                      osi_Log1(afsd_logp, "rx_Write succeeded %d",temp);
!                 }
                  nbytes -= wbytes;
              }	/* while more bytes to write */
! 		}		/* if RPC started successfully */
          else {
              osi_Log1(afsd_logp, "StartRXAFS_StoreData failed (%lX)",code);
          }
! 		if (code == 0) {
! 			code = EndRXAFS_StoreData(callp, &outStatus, &volSync);
              if (code)
                  osi_Log1(afsd_logp, "EndRXAFS_StoreData failed (%lX)",code);
          }
          code = rx_EndCall(callp, code);
          osi_Log0(afsd_logp, "CALL StoreData DONE");
                  
! 	} while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync, NULL, NULL, code));
      code = cm_MapRPCError(code, reqp);
          
      /* now, clean up our state */
      lock_ObtainMutex(&scp->mx);
  
! 	cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STOREDATA_EXCL);
  
! 	if (code == 0) {
! 		/* now, here's something a little tricky: in AFS 3, a dirty
! 		 * length can't be directly stored, instead, a dirty chunk is
! 		 * stored that sets the file's size (by writing and by using
! 		 * the truncate-first option in the store call).
! 		 *
           * At this point, we've just finished a store, and so the trunc
! 		 * pos field is clean.  If the file's size at the server is at
! 		 * least as big as we think it should be, then we turn off the
! 		 * length dirty bit, since all the other dirty buffers must
! 		 * precede this one in the file.
           *
           * The file's desired size shouldn't be smaller than what's
! 		 * stored at the server now, since we just did the trunc pos
! 		 * store.
           *
           * We have to turn off the length dirty bit as soon as we can,
! 		 * so that we see updates made by other machines.
           */
! 		if (outStatus.Length >= scp->length.LowPart)
              scp->mask &= ~CM_SCACHEMASK_LENGTH;
! 		cm_MergeStatus(scp, &outStatus, &volSync, userp, 0);
! 	} else {
! 		if (code == CM_ERROR_SPACE)
! 			scp->flags |= CM_SCACHEFLAG_OUTOFSPACE;
! 		else if (code == CM_ERROR_QUOTA)
! 			scp->flags |= CM_SCACHEFLAG_OVERQUOTA;
! 	}
      lock_ReleaseMutex(&scp->mx);
      cm_ReleaseBIOD(&biod, 1);
! 	cm_ReleaseSCache(scp);
  
      return code;
  }
--- 159,221 ----
                  if (temp != wbytes) {
                      osi_Log2(afsd_logp, "rx_Write failed %d != %d",temp,wbytes);
                      code = -1;
!                     break;
!                 } else {
                      osi_Log1(afsd_logp, "rx_Write succeeded %d",temp);
!                 }       
                  nbytes -= wbytes;
              }	/* while more bytes to write */
!         }		/* if RPC started successfully */
          else {
              osi_Log1(afsd_logp, "StartRXAFS_StoreData failed (%lX)",code);
          }
!         if (code == 0) {
!             code = EndRXAFS_StoreData(callp, &outStatus, &volSync);
              if (code)
                  osi_Log1(afsd_logp, "EndRXAFS_StoreData failed (%lX)",code);
          }
          code = rx_EndCall(callp, code);
          osi_Log0(afsd_logp, "CALL StoreData DONE");
                  
!     } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync, NULL, NULL, code));
      code = cm_MapRPCError(code, reqp);
          
      /* now, clean up our state */
      lock_ObtainMutex(&scp->mx);
  
!     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STOREDATA_EXCL);
  
!     if (code == 0) {
!         /* now, here's something a little tricky: in AFS 3, a dirty
!          * length can't be directly stored, instead, a dirty chunk is
!          * stored that sets the file's size (by writing and by using
!          * the truncate-first option in the store call).
!          *
           * At this point, we've just finished a store, and so the trunc
!          * pos field is clean.  If the file's size at the server is at
!          * least as big as we think it should be, then we turn off the
!          * length dirty bit, since all the other dirty buffers must
!          * precede this one in the file.
           *
           * The file's desired size shouldn't be smaller than what's
!          * stored at the server now, since we just did the trunc pos
!          * store.
           *
           * We have to turn off the length dirty bit as soon as we can,
!          * so that we see updates made by other machines.
           */
!         if (outStatus.Length >= scp->length.LowPart)
              scp->mask &= ~CM_SCACHEMASK_LENGTH;
!         cm_MergeStatus(scp, &outStatus, &volSync, userp, 0);
!     } else {
!         if (code == CM_ERROR_SPACE)
!             scp->flags |= CM_SCACHEFLAG_OUTOFSPACE;
!         else if (code == CM_ERROR_QUOTA)
!             scp->flags |= CM_SCACHEFLAG_OVERQUOTA;
!     }
      lock_ReleaseMutex(&scp->mx);
      cm_ReleaseBIOD(&biod, 1);
!     cm_ReleaseSCache(scp);
  
      return code;
  }
***************
*** 229,299 ****
      AFSStoreStatus inStatus;
      AFSVolSync volSync;
      AFSFid tfid;
! 	long code;
! 	long truncPos;
! 	cm_conn_t *connp;
      struct rx_call *callp;
  
! 	/* Serialize StoreData RPC's; for rationale see cm_scache.c */
! 	(void) cm_SyncOp(scp, NULL, userp, reqp, 0,
                       CM_SCACHESYNC_STOREDATA_EXCL);
  
! 	/* prepare the output status for the store */
! 	inStatus.Mask = AFS_SETMODTIME;
! 	inStatus.ClientModTime = scp->clientModTime;
! 	scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
  
! 	/* calculate truncation position */
      truncPos = scp->length.LowPart;
      if ((scp->mask & CM_SCACHEMASK_TRUNCPOS)
           && scp->truncPos.LowPart < (unsigned long) truncPos)
          truncPos = scp->truncPos.LowPart;
! 	scp->mask &= ~CM_SCACHEMASK_TRUNCPOS;
!                 
! 	lock_ReleaseMutex(&scp->mx);
  
! 	cm_AFSFidFromFid(&tfid, &scp->fid);
  
      /* now we're ready to do the store operation */
      do {
! 		code = cm_Conn(&scp->fid, userp, reqp, &connp);
          if (code) 
              continue;
  		
! 		callp = rx_NewCall(connp->callp);
  
          code = StartRXAFS_StoreData(callp, &tfid, &inStatus,
                                      0, 0, truncPos);
  
! 		if (code == 0)
! 			code = EndRXAFS_StoreData(callp, &outStatus, &volSync);
          code = rx_EndCall(callp, code);
! 	} while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync, NULL, NULL, code));
      code = cm_MapRPCError(code, reqp);
          
      /* now, clean up our state */
      lock_ObtainMutex(&scp->mx);
  
! 	cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STOREDATA_EXCL);
  
! 	if (code == 0) {
! 		/*
! 		 * For explanation of handling of CM_SCACHEMASK_LENGTH,
! 		 * see cm_BufWrite().
! 		 */
! 		if (outStatus.Length >= scp->length.LowPart)
              scp->mask &= ~CM_SCACHEMASK_LENGTH;
! 		cm_MergeStatus(scp, &outStatus, &volSync, userp, 0);
! 	}
  
! 	return code;
  }
  
  long cm_BufRead(cm_buf_t *bufp, long nbytes, long *bytesReadp, cm_user_t *userp)
  {
! 	*bytesReadp = buf_bufferSize;
  
! 	/* now return a code that means that I/O is done */
      return 0;
  }
  
--- 231,304 ----
      AFSStoreStatus inStatus;
      AFSVolSync volSync;
      AFSFid tfid;
!     long code;
!     long truncPos;
!     cm_conn_t *connp;
      struct rx_call *callp;
  
!     /* Serialize StoreData RPC's; for rationale see cm_scache.c */
!     (void) cm_SyncOp(scp, NULL, userp, reqp, 0,
                       CM_SCACHESYNC_STOREDATA_EXCL);
  
!     /* prepare the output status for the store */
!     inStatus.Mask = AFS_SETMODTIME;
!     inStatus.ClientModTime = scp->clientModTime;
!     scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
  
!     /* calculate truncation position */
      truncPos = scp->length.LowPart;
      if ((scp->mask & CM_SCACHEMASK_TRUNCPOS)
           && scp->truncPos.LowPart < (unsigned long) truncPos)
          truncPos = scp->truncPos.LowPart;
!     scp->mask &= ~CM_SCACHEMASK_TRUNCPOS;
  
!     lock_ReleaseMutex(&scp->mx);
! 
!     cm_AFSFidFromFid(&tfid, &scp->fid);
  
      /* now we're ready to do the store operation */
      do {
!         code = cm_Conn(&scp->fid, userp, reqp, &connp);
          if (code) 
              continue;
  		
!         lock_ObtainMutex(&connp->mx);
!         callp = rx_NewCall(connp->callp);
!         lock_ReleaseMutex(&connp->mx);
  
          code = StartRXAFS_StoreData(callp, &tfid, &inStatus,
                                      0, 0, truncPos);
  
!         if (code == 0)
!             code = EndRXAFS_StoreData(callp, &outStatus, &volSync);
          code = rx_EndCall(callp, code);
! 
!     } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync, NULL, NULL, code));
      code = cm_MapRPCError(code, reqp);
          
      /* now, clean up our state */
      lock_ObtainMutex(&scp->mx);
  
!     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STOREDATA_EXCL);
  
!     if (code == 0) {
!         /*
!          * For explanation of handling of CM_SCACHEMASK_LENGTH,
!          * see cm_BufWrite().
!          */
!         if (outStatus.Length >= scp->length.LowPart)
              scp->mask &= ~CM_SCACHEMASK_LENGTH;
!         cm_MergeStatus(scp, &outStatus, &volSync, userp, 0);
!     }
  
!     return code;
  }
  
  long cm_BufRead(cm_buf_t *bufp, long nbytes, long *bytesReadp, cm_user_t *userp)
  {
!     *bytesReadp = buf_bufferSize;
  
!     /* now return a code that means that I/O is done */
      return 0;
  }
  
***************
*** 302,319 ****
   */
  long cm_BufStabilize(void *parmp, cm_user_t *userp, cm_req_t *reqp)
  {
! 	cm_scache_t *scp;
      long code;
  
      scp = parmp;
          
! 	lock_ObtainMutex(&scp->mx);
      code = cm_SyncOp(scp, NULL, userp, reqp, 0, 
                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_SETSIZE);
! 	if (code) {
! 		lock_ReleaseMutex(&scp->mx);
          return code;
! 	}
          
      return 0;
  }
--- 307,324 ----
   */
  long cm_BufStabilize(void *parmp, cm_user_t *userp, cm_req_t *reqp)
  {
!     cm_scache_t *scp;
      long code;
  
      scp = parmp;
          
!     lock_ObtainMutex(&scp->mx);
      code = cm_SyncOp(scp, NULL, userp, reqp, 0, 
                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_SETSIZE);
!     if (code) {
!         lock_ReleaseMutex(&scp->mx);
          return code;
!     }
          
      return 0;
  }
***************
*** 321,338 ****
  /* undoes the work that cm_BufStabilize does: releases lock so things can change again */
  long cm_BufUnstabilize(void *parmp, cm_user_t *userp)
  {
! 	cm_scache_t *scp;
          
      scp = parmp;
          
      lock_ReleaseMutex(&scp->mx);
          
! 	/* always succeeds */
      return 0;
  }
  
  cm_buf_ops_t cm_bufOps = {
! 	cm_BufWrite,
      cm_BufRead,
      cm_BufStabilize,
      cm_BufUnstabilize
--- 326,343 ----
  /* undoes the work that cm_BufStabilize does: releases lock so things can change again */
  long cm_BufUnstabilize(void *parmp, cm_user_t *userp)
  {
!     cm_scache_t *scp;
          
      scp = parmp;
          
      lock_ReleaseMutex(&scp->mx);
          
!     /* always succeeds */
      return 0;
  }
  
  cm_buf_ops_t cm_bufOps = {
!     cm_BufWrite,
      cm_BufRead,
      cm_BufStabilize,
      cm_BufUnstabilize
***************
*** 340,349 ****
  
  int cm_InitDCache(long chunkSize, long nbuffers)
  {
! 	lock_InitializeMutex(&cm_bufGetMutex, "buf_Get mutex");
! 	if (nbuffers) 
          buf_nbuffers = nbuffers;
! 	return buf_Init(&cm_bufOps);
  }
  
  /* check to see if we have an up-to-date buffer.  The buffer must have
--- 345,354 ----
  
  int cm_InitDCache(long chunkSize, long nbuffers)
  {
!     lock_InitializeMutex(&cm_bufGetMutex, "buf_Get mutex");
!     if (nbuffers) 
          buf_nbuffers = nbuffers;
!     return buf_Init(&cm_bufOps);
  }
  
  /* check to see if we have an up-to-date buffer.  The buffer must have
***************
*** 357,400 ****
   */
  int cm_HaveBuffer(cm_scache_t *scp, cm_buf_t *bufp, int isBufLocked)
  {
! 	int code;
! 	if (!cm_HaveCallback(scp))
! 		return 0;
! 	if ((bufp->cmFlags
! 	     & (CM_BUF_CMFETCHING | CM_BUF_CMFULLYFETCHED))
! 		== (CM_BUF_CMFETCHING | CM_BUF_CMFULLYFETCHED))
! 		return 1;
!        	if (bufp->dataVersion == scp->dataVersion)
!         	return 1;
! 	if (!isBufLocked) {
! 		code = lock_TryMutex(&bufp->mx);
          if (code == 0) {
! 			/* don't have the lock, and can't lock it, then
               * return failure.
               */
              return 0;
          }
      }
  
! 	/* remember dirty flag for later */
! 	code = bufp->flags & CM_BUF_DIRTY;
  
! 	/* release lock if we obtained it here */
! 	if (!isBufLocked) 
          lock_ReleaseMutex(&bufp->mx);
  
! 	/* if buffer was dirty, buffer is acceptable for use */
!         if (code) 
!             return 1;
!         else 
!             return 0;
  }
  
  /* used when deciding whether to do a prefetch or not */
  long cm_CheckFetchRange(cm_scache_t *scp, osi_hyper_t *startBasep, long length,
! 	cm_user_t *up, cm_req_t *reqp, osi_hyper_t *realBasep)
  {
! 	osi_hyper_t toffset;
      osi_hyper_t tbase;
      long code;
      cm_buf_t *bp;
--- 362,405 ----
   */
  int cm_HaveBuffer(cm_scache_t *scp, cm_buf_t *bufp, int isBufLocked)
  {
!     int code;
!     if (!cm_HaveCallback(scp))
!         return 0;
!     if ((bufp->cmFlags
!           & (CM_BUF_CMFETCHING | CM_BUF_CMFULLYFETCHED))
!          == (CM_BUF_CMFETCHING | CM_BUF_CMFULLYFETCHED))
!         return 1;
!     if (bufp->dataVersion == scp->dataVersion)
!         return 1;
!     if (!isBufLocked) {
!         code = lock_TryMutex(&bufp->mx);
          if (code == 0) {
!             /* don't have the lock, and can't lock it, then
               * return failure.
               */
              return 0;
          }
      }
  
!     /* remember dirty flag for later */
!     code = bufp->flags & CM_BUF_DIRTY;
  
!     /* release lock if we obtained it here */
!     if (!isBufLocked) 
          lock_ReleaseMutex(&bufp->mx);
  
!     /* if buffer was dirty, buffer is acceptable for use */
!     if (code) 
!         return 1;
!     else 
!         return 0;
  }
  
  /* used when deciding whether to do a prefetch or not */
  long cm_CheckFetchRange(cm_scache_t *scp, osi_hyper_t *startBasep, long length,
!                         cm_user_t *up, cm_req_t *reqp, osi_hyper_t *realBasep)
  {
!     osi_hyper_t toffset;
      osi_hyper_t tbase;
      long code;
      cm_buf_t *bp;
***************
*** 403,439 ****
      /* now scan all buffers in the range, looking for any that look like
       * they need work.
       */
! 	tbase = *startBasep;
! 	stop = 0;
! 	lock_ObtainMutex(&scp->mx);
      while(length > 0) {
! 		/* get callback so we can do a meaningful dataVersion comparison */
          code = cm_SyncOp(scp, NULL, up, reqp, 0,
                           CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
! 		if (code) {
! 			scp->flags &= ~CM_SCACHEFLAG_PREFETCHING;
! 			lock_ReleaseMutex(&scp->mx);
              return code;
          }
                  
          if (LargeIntegerGreaterThanOrEqualTo(tbase, scp->length)) {
! 			/* we're past the end of file */
              break;
          }
  
! 		bp = buf_Find(scp, &tbase);
! 		/* We cheat slightly by not locking the bp mutex. */
          if (bp) {
              if ((bp->cmFlags
! 			      & (CM_BUF_CMFETCHING | CM_BUF_CMSTORING)) == 0
                   && bp->dataVersion != scp->dataVersion)
                  stop = 1;
              buf_Release(bp);
! 		}
          else 
              stop = 1;
  
! 		/* if this buffer is essentially guaranteed to require a fetch,
           * break out here and return this position.
           */
          if (stop) 
--- 408,444 ----
      /* now scan all buffers in the range, looking for any that look like
       * they need work.
       */
!     tbase = *startBasep;
!     stop = 0;
!     lock_ObtainMutex(&scp->mx);
      while(length > 0) {
!         /* get callback so we can do a meaningful dataVersion comparison */
          code = cm_SyncOp(scp, NULL, up, reqp, 0,
                           CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
!         if (code) {
!             scp->flags &= ~CM_SCACHEFLAG_PREFETCHING;
!             lock_ReleaseMutex(&scp->mx);
              return code;
          }
                  
          if (LargeIntegerGreaterThanOrEqualTo(tbase, scp->length)) {
!             /* we're past the end of file */
              break;
          }
  
!         bp = buf_Find(scp, &tbase);
!         /* We cheat slightly by not locking the bp mutex. */
          if (bp) {
              if ((bp->cmFlags
!                   & (CM_BUF_CMFETCHING | CM_BUF_CMSTORING)) == 0
                   && bp->dataVersion != scp->dataVersion)
                  stop = 1;
              buf_Release(bp);
!         }
          else 
              stop = 1;
  
!         /* if this buffer is essentially guaranteed to require a fetch,
           * break out here and return this position.
           */
          if (stop) 
***************
*** 449,461 ****
       * particular buffer in the range that definitely needs to be fetched.
       */
      if (stop == 0) {
! 		/* return non-zero code since realBasep won't be valid */
! 		scp->flags &= ~CM_SCACHEFLAG_PREFETCHING;
! 		code = -1;
!     }
      else {
! 		/* successfully found a page that will need fetching */
! 		*realBasep = tbase;
          code = 0;
      }
      lock_ReleaseMutex(&scp->mx);
--- 454,466 ----
       * particular buffer in the range that definitely needs to be fetched.
       */
      if (stop == 0) {
!         /* return non-zero code since realBasep won't be valid */
!         scp->flags &= ~CM_SCACHEFLAG_PREFETCHING;
!         code = -1;
!     }   
      else {
!         /* successfully found a page that will need fetching */
!         *realBasep = tbase;
          code = 0;
      }
      lock_ReleaseMutex(&scp->mx);
***************
*** 463,541 ****
  }
  
  void cm_BkgStore(cm_scache_t *scp, long p1, long p2, long p3, long p4,
! 	cm_user_t *userp)
  {
! 	osi_hyper_t toffset;
      long length;
! 	cm_req_t req;
  
! 	cm_InitReq(&req);
! 	req.flags |= CM_REQ_NORETRY;
  
      toffset.LowPart = p1;
      toffset.HighPart = p2;
      length = p3;
  
! 	osi_Log2(afsd_logp, "Starting BKG store vp 0x%x, base 0x%x", scp, p1);
  
! 	cm_BufWrite(&scp->fid, &toffset, length, /* flags */ 0, userp, &req);
  
! 	lock_ObtainMutex(&scp->mx);
! 	cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_ASYNCSTORE);
      lock_ReleaseMutex(&scp->mx);
  }
  
  void cm_ClearPrefetchFlag(long code, cm_scache_t *scp, osi_hyper_t *base)
  {
! 	osi_hyper_t thyper;
  
! 	if (code == 0) {
! 		thyper.LowPart = cm_chunkSize;
! 		thyper.HighPart = 0;
! 		thyper =  LargeIntegerAdd(*base, thyper);
! 		thyper.LowPart &= (-cm_chunkSize);
! 		if (LargeIntegerGreaterThan(*base, scp->prefetch.base))
              scp->prefetch.base = *base;
! 		if (LargeIntegerGreaterThan(thyper, scp->prefetch.end))
              scp->prefetch.end = thyper;
! 	}
! 	scp->flags &= ~CM_SCACHEFLAG_PREFETCHING;
  }
  
  /* do the prefetch */
  void cm_BkgPrefetch(cm_scache_t *scp, long p1, long p2, long p3, long p4,
! 	cm_user_t *userp)
  {
! 	long length;
      osi_hyper_t base;
      long code;
      cm_buf_t *bp;
! 	int cpff = 0;			/* cleared prefetch flag */
! 	cm_req_t req;
  
! 	cm_InitReq(&req);
! 	req.flags |= CM_REQ_NORETRY;
          
! 	base.LowPart = p1;
      base.HighPart = p2;
      length = p3;
          
! 	osi_Log2(afsd_logp, "Starting BKG prefetch vp 0x%x, base 0x%x", scp, p1);
  
      code = buf_Get(scp, &base, &bp);
  
! 	lock_ObtainMutex(&scp->mx);
  
      if (code || (bp->cmFlags & CM_BUF_CMFETCHING)) {
! 		scp->flags &= ~CM_SCACHEFLAG_PREFETCHING;
! 		lock_ReleaseMutex(&scp->mx);
! 		return;
! 	}
  
      code = cm_GetBuffer(scp, bp, &cpff, userp, &req);
! 	if (!cpff) 
          cm_ClearPrefetchFlag(code, scp, &base);
! 	lock_ReleaseMutex(&scp->mx);
      buf_Release(bp);
      return;
  }
--- 468,546 ----
  }
  
  void cm_BkgStore(cm_scache_t *scp, long p1, long p2, long p3, long p4,
!                  cm_user_t *userp)
  {
!     osi_hyper_t toffset;
      long length;
!     cm_req_t req;
  
!     cm_InitReq(&req);
!     req.flags |= CM_REQ_NORETRY;
  
      toffset.LowPart = p1;
      toffset.HighPart = p2;
      length = p3;
  
!     osi_Log2(afsd_logp, "Starting BKG store vp 0x%x, base 0x%x", scp, p1);
  
!     cm_BufWrite(&scp->fid, &toffset, length, /* flags */ 0, userp, &req);
  
!     lock_ObtainMutex(&scp->mx);
!     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_ASYNCSTORE);
      lock_ReleaseMutex(&scp->mx);
  }
  
  void cm_ClearPrefetchFlag(long code, cm_scache_t *scp, osi_hyper_t *base)
  {
!     osi_hyper_t thyper;
  
!     if (code == 0) {
!         thyper.LowPart = cm_chunkSize;
!         thyper.HighPart = 0;
!         thyper =  LargeIntegerAdd(*base, thyper);
!         thyper.LowPart &= (-cm_chunkSize);
!         if (LargeIntegerGreaterThan(*base, scp->prefetch.base))
              scp->prefetch.base = *base;
!         if (LargeIntegerGreaterThan(thyper, scp->prefetch.end))
              scp->prefetch.end = thyper;
!     }
!     scp->flags &= ~CM_SCACHEFLAG_PREFETCHING;
  }
  
  /* do the prefetch */
  void cm_BkgPrefetch(cm_scache_t *scp, long p1, long p2, long p3, long p4,
!                     cm_user_t *userp)
  {
!     long length;
      osi_hyper_t base;
      long code;
      cm_buf_t *bp;
!     int cpff = 0;			/* cleared prefetch flag */
!     cm_req_t req;
  
!     cm_InitReq(&req);
!     req.flags |= CM_REQ_NORETRY;
          
!     base.LowPart = p1;
      base.HighPart = p2;
      length = p3;
          
!     osi_Log2(afsd_logp, "Starting BKG prefetch vp 0x%x, base 0x%x", scp, p1);
  
      code = buf_Get(scp, &base, &bp);
  
!     lock_ObtainMutex(&scp->mx);
  
      if (code || (bp->cmFlags & CM_BUF_CMFETCHING)) {
!         scp->flags &= ~CM_SCACHEFLAG_PREFETCHING;
!         lock_ReleaseMutex(&scp->mx);
!         return;
!     }
  
      code = cm_GetBuffer(scp, bp, &cpff, userp, &req);
!     if (!cpff) 
          cm_ClearPrefetchFlag(code, scp, &base);
!     lock_ReleaseMutex(&scp->mx);
      buf_Release(bp);
      return;
  }
***************
*** 544,579 ****
   * do a prefetch.
   */
  void cm_ConsiderPrefetch(cm_scache_t *scp, osi_hyper_t *offsetp,
! 	cm_user_t *userp, cm_req_t *reqp)
  {
! 	long code;
      osi_hyper_t realBase;
      osi_hyper_t readBase;
          
      readBase = *offsetp;
! 	/* round up to chunk boundary */
! 	readBase.LowPart += (cm_chunkSize-1);
! 	readBase.LowPart &= (-cm_chunkSize);
  
! 	lock_ObtainMutex(&scp->mx);
! 	if ((scp->flags & CM_SCACHEFLAG_PREFETCHING)
           || LargeIntegerLessThanOrEqualTo(readBase, scp->prefetch.base)) {
! 		lock_ReleaseMutex(&scp->mx);
          return;
! 	}
! 	scp->flags |= CM_SCACHEFLAG_PREFETCHING;
  
! 	/* start the scan at the latter of the end of this read or
       * the end of the last fetched region.
       */
! 	if (LargeIntegerGreaterThan(scp->prefetch.end, readBase))
          readBase = scp->prefetch.end;
  
      lock_ReleaseMutex(&scp->mx);
  
      code = cm_CheckFetchRange(scp, &readBase, cm_chunkSize, userp, reqp,
!                                   &realBase);
! 	if (code) 
          return;	/* can't find something to prefetch */
  
      osi_Log2(afsd_logp, "BKG Prefetch request vp 0x%x, base 0x%x",
--- 549,584 ----
   * do a prefetch.
   */
  void cm_ConsiderPrefetch(cm_scache_t *scp, osi_hyper_t *offsetp,
!                          cm_user_t *userp, cm_req_t *reqp)
  {
!     long code;
      osi_hyper_t realBase;
      osi_hyper_t readBase;
          
      readBase = *offsetp;
!     /* round up to chunk boundary */
!     readBase.LowPart += (cm_chunkSize-1);
!     readBase.LowPart &= (-cm_chunkSize);
  
!     lock_ObtainMutex(&scp->mx);
!     if ((scp->flags & CM_SCACHEFLAG_PREFETCHING)
           || LargeIntegerLessThanOrEqualTo(readBase, scp->prefetch.base)) {
!         lock_ReleaseMutex(&scp->mx);
          return;
!     }
!     scp->flags |= CM_SCACHEFLAG_PREFETCHING;
  
!     /* start the scan at the latter of the end of this read or
       * the end of the last fetched region.
       */
!     if (LargeIntegerGreaterThan(scp->prefetch.end, readBase))
          readBase = scp->prefetch.end;
  
      lock_ReleaseMutex(&scp->mx);
  
      code = cm_CheckFetchRange(scp, &readBase, cm_chunkSize, userp, reqp,
!                               &realBase);
!     if (code) 
          return;	/* can't find something to prefetch */
  
      osi_Log2(afsd_logp, "BKG Prefetch request vp 0x%x, base 0x%x",
***************
*** 595,601 ****
   * is being written out.
   */
  long cm_SetupStoreBIOD(cm_scache_t *scp, osi_hyper_t *inOffsetp, long inSize,
! 	cm_bulkIO_t *biop, cm_user_t *userp, cm_req_t *reqp)
  {
      cm_buf_t *bufp;
      osi_queueData_t *qdp;
--- 600,606 ----
   * is being written out.
   */
  long cm_SetupStoreBIOD(cm_scache_t *scp, osi_hyper_t *inOffsetp, long inSize,
!                        cm_bulkIO_t *biop, cm_user_t *userp, cm_req_t *reqp)
  {
      cm_buf_t *bufp;
      osi_queueData_t *qdp;
***************
*** 608,682 ****
      long code;
      long flags;			/* flags to cm_SyncOp */
          
! 	/* clear things out */
! 	biop->scp = scp;		/* don't hold */
      biop->offset = *inOffsetp;
      biop->length = 0;
      biop->bufListp = NULL;
      biop->bufListEndp = NULL;
! 	biop->reserved = 0;
  
! 	/* reserve a chunk's worth of buffers */
! 	lock_ReleaseMutex(&scp->mx);
! 	buf_ReserveBuffers(cm_chunkSize / buf_bufferSize);
! 	lock_ObtainMutex(&scp->mx);
  
      bufp = NULL;
!     for(temp = 0; temp < inSize; temp += buf_bufferSize, bufp = NULL) {
! 		thyper.HighPart = 0;
! 		thyper.LowPart = temp;
          tbase = LargeIntegerAdd(*inOffsetp, thyper);
  
          bufp = buf_Find(scp, &tbase);
          if (bufp) {
! 			/* get buffer mutex and scp mutex safely */
! 			lock_ReleaseMutex(&scp->mx);
! 			lock_ObtainMutex(&bufp->mx);
! 			lock_ObtainMutex(&scp->mx);
! 
! 			flags = CM_SCACHESYNC_NEEDCALLBACK
!                         	| CM_SCACHESYNC_GETSTATUS
!                                 | CM_SCACHESYNC_STOREDATA
!                                 | CM_SCACHESYNC_BUFLOCKED;
! 			code = cm_SyncOp(scp, bufp, userp, reqp, 0, flags); 
              if (code) {
! 				lock_ReleaseMutex(&bufp->mx);
                  buf_Release(bufp);
! 				buf_UnreserveBuffers(cm_chunkSize / buf_bufferSize);
                  return code;
!             }
                          
! 			/* if the buffer is dirty, we're done */
              if (bufp->flags & CM_BUF_DIRTY) {
                  osi_assertx(!(bufp->flags & CM_BUF_WRITING),
                              "WRITING w/o CMSTORING in SetupStoreBIOD");
! 				bufp->flags |= CM_BUF_WRITING;
! 				break;
              }
  
! 			/* this buffer is clean, so there's no reason to process it */
! 			cm_SyncOpDone(scp, bufp, flags);
! 			lock_ReleaseMutex(&bufp->mx);
! 			buf_Release(bufp);
!         }
      }
  
! 	biop->reserved = 1;
          
      /* if we get here, if bufp is null, we didn't find any dirty buffers
! 	 * that weren't already being stored back, so we just quit now.
       */
!         if (!bufp) {
!         	return 0;
! 	}
  
! 	/* don't need buffer mutex any more */
! 	lock_ReleaseMutex(&bufp->mx);
          
! 	/* put this element in the list */
      qdp = osi_QDAlloc();
      osi_SetQData(qdp, bufp);
! 	/* don't have to hold bufp, since held by buf_Find above */
      osi_QAddH((osi_queue_t **) &biop->bufListp,
                (osi_queue_t **) &biop->bufListEndp,
                &qdp->q);
--- 613,687 ----
      long code;
      long flags;			/* flags to cm_SyncOp */
          
!     /* clear things out */
!     biop->scp = scp;		/* don't hold */
      biop->offset = *inOffsetp;
      biop->length = 0;
      biop->bufListp = NULL;
      biop->bufListEndp = NULL;
!     biop->reserved = 0;
  
!     /* reserve a chunk's worth of buffers */
!     lock_ReleaseMutex(&scp->mx);
!     buf_ReserveBuffers(cm_chunkSize / buf_bufferSize);
!     lock_ObtainMutex(&scp->mx);
  
      bufp = NULL;
!     for (temp = 0; temp < inSize; temp += buf_bufferSize, bufp = NULL) {
!         thyper.HighPart = 0;
!         thyper.LowPart = temp;
          tbase = LargeIntegerAdd(*inOffsetp, thyper);
  
          bufp = buf_Find(scp, &tbase);
          if (bufp) {
!             /* get buffer mutex and scp mutex safely */
!             lock_ReleaseMutex(&scp->mx);
!             lock_ObtainMutex(&bufp->mx);
!             lock_ObtainMutex(&scp->mx);
! 
!             flags = CM_SCACHESYNC_NEEDCALLBACK
!                 | CM_SCACHESYNC_GETSTATUS
!                     | CM_SCACHESYNC_STOREDATA
!                         | CM_SCACHESYNC_BUFLOCKED;
!             code = cm_SyncOp(scp, bufp, userp, reqp, 0, flags); 
              if (code) {
!                 lock_ReleaseMutex(&bufp->mx);
                  buf_Release(bufp);
!                 buf_UnreserveBuffers(cm_chunkSize / buf_bufferSize);
                  return code;
!             }   
                          
!             /* if the buffer is dirty, we're done */
              if (bufp->flags & CM_BUF_DIRTY) {
                  osi_assertx(!(bufp->flags & CM_BUF_WRITING),
                              "WRITING w/o CMSTORING in SetupStoreBIOD");
!                 bufp->flags |= CM_BUF_WRITING;
!                 break;
              }
  
!             /* this buffer is clean, so there's no reason to process it */
!             cm_SyncOpDone(scp, bufp, flags);
!             lock_ReleaseMutex(&bufp->mx);
!             buf_Release(bufp);
!         }       
      }
  
!     biop->reserved = 1;
          
      /* if we get here, if bufp is null, we didn't find any dirty buffers
!      * that weren't already being stored back, so we just quit now.
       */
!     if (!bufp) {
!         return 0;
!     }
  
!     /* don't need buffer mutex any more */
!     lock_ReleaseMutex(&bufp->mx);
          
!     /* put this element in the list */
      qdp = osi_QDAlloc();
      osi_SetQData(qdp, bufp);
!     /* don't have to hold bufp, since held by buf_Find above */
      osi_QAddH((osi_queue_t **) &biop->bufListp,
                (osi_queue_t **) &biop->bufListEndp,
                &qdp->q);
***************
*** 684,749 ****
      firstModOffset = bufp->offset;
      biop->offset = firstModOffset;
  
! 	/* compute the window surrounding *inOffsetp of size cm_chunkSize */
! 	scanStart = *inOffsetp;
      scanStart.LowPart &= (-cm_chunkSize);
! 	thyper.LowPart = cm_chunkSize;
      thyper.HighPart = 0;
! 	scanEnd = LargeIntegerAdd(scanStart, thyper);
  
! 	flags = CM_SCACHESYNC_NEEDCALLBACK
! 		| CM_SCACHESYNC_GETSTATUS
          | CM_SCACHESYNC_STOREDATA
          | CM_SCACHESYNC_BUFLOCKED
          | CM_SCACHESYNC_NOWAIT;
  
! 	/* start by looking backwards until scanStart */
! 	thyper.HighPart = 0;		/* hyper version of buf_bufferSize */
      thyper.LowPart = buf_bufferSize;
! 	tbase = LargeIntegerSubtract(firstModOffset, thyper);
      while(LargeIntegerGreaterThanOrEqualTo(tbase, scanStart)) {
          /* see if we can find the buffer */
! 		bufp = buf_Find(scp, &tbase);
          if (!bufp) 
              break;
  
! 		/* try to lock it, and quit if we can't (simplifies locking) */
          code = lock_TryMutex(&bufp->mx);
          if (code == 0) {
! 			buf_Release(bufp);
              break;
          }
                  
          code = cm_SyncOp(scp, bufp, userp, reqp, 0, flags);
          if (code) {
! 			lock_ReleaseMutex(&bufp->mx);
! 			buf_Release(bufp);
              break;
          }
                  
! 		if (!(bufp->flags & CM_BUF_DIRTY)) {
! 			/* buffer is clean, so we shouldn't add it */
! 			cm_SyncOpDone(scp, bufp, flags);
! 			lock_ReleaseMutex(&bufp->mx);
! 			buf_Release(bufp);
              break;
          }
  
! 		/* don't need buffer mutex any more */
! 		lock_ReleaseMutex(&bufp->mx);
  
          /* we have a dirty buffer ready for storing.  Add it to the tail
           * of the list, since it immediately precedes all of the disk
           * addresses we've already collected.
           */
! 		qdp = osi_QDAlloc();
          osi_SetQData(qdp, bufp);
          /* no buf_hold necessary, since we have it held from buf_Find */
          osi_QAddT((osi_queue_t **) &biop->bufListp,
                    (osi_queue_t **) &biop->bufListEndp,
                    &qdp->q);
  
! 		/* update biod info describing the transfer */
          biop->offset = LargeIntegerSubtract(biop->offset, thyper);
          biop->length += buf_bufferSize;
  
--- 689,754 ----
      firstModOffset = bufp->offset;
      biop->offset = firstModOffset;
  
!     /* compute the window surrounding *inOffsetp of size cm_chunkSize */
!     scanStart = *inOffsetp;
      scanStart.LowPart &= (-cm_chunkSize);
!     thyper.LowPart = cm_chunkSize;
      thyper.HighPart = 0;
!     scanEnd = LargeIntegerAdd(scanStart, thyper);
  
!     flags = CM_SCACHESYNC_NEEDCALLBACK
!         | CM_SCACHESYNC_GETSTATUS
          | CM_SCACHESYNC_STOREDATA
          | CM_SCACHESYNC_BUFLOCKED
          | CM_SCACHESYNC_NOWAIT;
  
!     /* start by looking backwards until scanStart */
!     thyper.HighPart = 0;		/* hyper version of buf_bufferSize */
      thyper.LowPart = buf_bufferSize;
!     tbase = LargeIntegerSubtract(firstModOffset, thyper);
      while(LargeIntegerGreaterThanOrEqualTo(tbase, scanStart)) {
          /* see if we can find the buffer */
!         bufp = buf_Find(scp, &tbase);
          if (!bufp) 
              break;
  
!         /* try to lock it, and quit if we can't (simplifies locking) */
          code = lock_TryMutex(&bufp->mx);
          if (code == 0) {
!             buf_Release(bufp);
              break;
          }
                  
          code = cm_SyncOp(scp, bufp, userp, reqp, 0, flags);
          if (code) {
!             lock_ReleaseMutex(&bufp->mx);
!             buf_Release(bufp);
              break;
          }
                  
!         if (!(bufp->flags & CM_BUF_DIRTY)) {
!             /* buffer is clean, so we shouldn't add it */
!             cm_SyncOpDone(scp, bufp, flags);
!             lock_ReleaseMutex(&bufp->mx);
!             buf_Release(bufp);
              break;
          }
  
!         /* don't need buffer mutex any more */
!         lock_ReleaseMutex(&bufp->mx);
  
          /* we have a dirty buffer ready for storing.  Add it to the tail
           * of the list, since it immediately precedes all of the disk
           * addresses we've already collected.
           */
!         qdp = osi_QDAlloc();
          osi_SetQData(qdp, bufp);
          /* no buf_hold necessary, since we have it held from buf_Find */
          osi_QAddT((osi_queue_t **) &biop->bufListp,
                    (osi_queue_t **) &biop->bufListEndp,
                    &qdp->q);
  
!         /* update biod info describing the transfer */
          biop->offset = LargeIntegerSubtract(biop->offset, thyper);
          biop->length += buf_bufferSize;
  
***************
*** 751,803 ****
          tbase = LargeIntegerSubtract(tbase, thyper);
      }	/* while loop looking for pages preceding the one we found */
  
! 	/* now, find later dirty, contiguous pages, and add them to the list */
! 	thyper.HighPart = 0;		/* hyper version of buf_bufferSize */
      thyper.LowPart = buf_bufferSize;
! 	tbase = LargeIntegerAdd(firstModOffset, thyper);
      while(LargeIntegerLessThan(tbase, scanEnd)) {
! 		/* see if we can find the buffer */
! 		bufp = buf_Find(scp, &tbase);
          if (!bufp) 
              break;
  
! 		/* try to lock it, and quit if we can't (simplifies locking) */
          code = lock_TryMutex(&bufp->mx);
          if (code == 0) {
! 			buf_Release(bufp);
              break;
          }
  
          code = cm_SyncOp(scp, bufp, userp, reqp, 0, flags);
          if (code) {
! 			lock_ReleaseMutex(&bufp->mx);
! 			buf_Release(bufp);
              break;
          }
                  
! 		if (!(bufp->flags & CM_BUF_DIRTY)) {
! 			/* buffer is clean, so we shouldn't add it */
! 			cm_SyncOpDone(scp, bufp, flags);
! 			lock_ReleaseMutex(&bufp->mx);
! 			buf_Release(bufp);
              break;
          }
  
! 		/* don't need buffer mutex any more */
! 		lock_ReleaseMutex(&bufp->mx);
  
          /* we have a dirty buffer ready for storing.  Add it to the head
           * of the list, since it immediately follows all of the disk
           * addresses we've already collected.
           */
! 		qdp = osi_QDAlloc();
          osi_SetQData(qdp, bufp);
          /* no buf_hold necessary, since we have it held from buf_Find */
          osi_QAddH((osi_queue_t **) &biop->bufListp,
                    (osi_queue_t **) &biop->bufListEndp,
                    &qdp->q);
  
! 		/* update biod info describing the transfer */
          biop->length += buf_bufferSize;
                  
          /* update loop pointer */
--- 756,808 ----
          tbase = LargeIntegerSubtract(tbase, thyper);
      }	/* while loop looking for pages preceding the one we found */
  
!     /* now, find later dirty, contiguous pages, and add them to the list */
!     thyper.HighPart = 0;		/* hyper version of buf_bufferSize */
      thyper.LowPart = buf_bufferSize;
!     tbase = LargeIntegerAdd(firstModOffset, thyper);
      while(LargeIntegerLessThan(tbase, scanEnd)) {
!         /* see if we can find the buffer */
!         bufp = buf_Find(scp, &tbase);
          if (!bufp) 
              break;
  
!         /* try to lock it, and quit if we can't (simplifies locking) */
          code = lock_TryMutex(&bufp->mx);
          if (code == 0) {
!             buf_Release(bufp);
              break;
          }
  
          code = cm_SyncOp(scp, bufp, userp, reqp, 0, flags);
          if (code) {
!             lock_ReleaseMutex(&bufp->mx);
!             buf_Release(bufp);
              break;
          }
                  
!         if (!(bufp->flags & CM_BUF_DIRTY)) {
!             /* buffer is clean, so we shouldn't add it */
!             cm_SyncOpDone(scp, bufp, flags);
!             lock_ReleaseMutex(&bufp->mx);
!             buf_Release(bufp);
              break;
          }
  
!         /* don't need buffer mutex any more */
!         lock_ReleaseMutex(&bufp->mx);
  
          /* we have a dirty buffer ready for storing.  Add it to the head
           * of the list, since it immediately follows all of the disk
           * addresses we've already collected.
           */
!         qdp = osi_QDAlloc();
          osi_SetQData(qdp, bufp);
          /* no buf_hold necessary, since we have it held from buf_Find */
          osi_QAddH((osi_queue_t **) &biop->bufListp,
                    (osi_queue_t **) &biop->bufListEndp,
                    &qdp->q);
  
!         /* update biod info describing the transfer */
          biop->length += buf_bufferSize;
                  
          /* update loop pointer */
***************
*** 816,822 ****
  long cm_SetupFetchBIOD(cm_scache_t *scp, osi_hyper_t *offsetp,
  			cm_bulkIO_t *biop, cm_user_t *up, cm_req_t *reqp)
  {
! 	long code;
      cm_buf_t *tbp;
      osi_hyper_t toffset;		/* a long long temp variable */
      osi_hyper_t pageBase;		/* base offset we're looking at */
--- 821,827 ----
  long cm_SetupFetchBIOD(cm_scache_t *scp, osi_hyper_t *offsetp,
  			cm_bulkIO_t *biop, cm_user_t *up, cm_req_t *reqp)
  {
!     long code;
      cm_buf_t *tbp;
      osi_hyper_t toffset;		/* a long long temp variable */
      osi_hyper_t pageBase;		/* base offset we're looking at */
***************
*** 828,913 ****
      osi_hyper_t fileSize;		/* the # of bytes in the file */
      osi_queueData_t *heldBufListp;	/* we hold all buffers in this list */
      osi_queueData_t *heldBufListEndp;	/* first one */
! 	int reserving;
  
      biop->scp = scp;
      biop->offset = *offsetp;
! 	/* null out the list of buffers */
      biop->bufListp = biop->bufListEndp = NULL;
! 	biop->reserved = 0;
  
! 	/* first lookup the file's length, so we know when to stop */
      code = cm_SyncOp(scp, NULL, up, reqp, 0, 
                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
      if (code) 
          return code;
          
! 	/* copy out size, since it may change */
      fileSize = scp->serverLength;
          
      lock_ReleaseMutex(&scp->mx);
  
! 	pageBase = *offsetp;
      collected = pageBase.LowPart & (cm_chunkSize - 1);
      heldBufListp = NULL;
      heldBufListEndp = NULL;
  
! 	/*
! 	 * Obtaining buffers can cause dirty buffers to be recycled, which
! 	 * can cause a storeback, so cannot be done while we have buffers
! 	 * reserved.
! 	 *
! 	 * To get around this, we get buffers twice.  Before reserving buffers,
! 	 * we obtain and release each one individually.  After reserving
! 	 * buffers, we try to obtain them again, but only by lookup, not by
! 	 * recycling.  If a buffer has gone away while we were waiting for
! 	 * the others, we just use whatever buffers we already have.
! 	 *
! 	 * On entry to this function, we are already holding a buffer, so we
! 	 * can't wait for reservation.  So we call buf_TryReserveBuffers()
! 	 * instead.  Not only that, we can't really even call buf_Get(), for
! 	 * the same reason.  We can't avoid that, though.  To avoid deadlock
! 	 * we allow only one thread to be executing the buf_Get()-buf_Release()
! 	 * sequence at a time.
! 	 */
  
! 	/* first hold all buffers, since we can't hold any locks in buf_Get */
      while (1) {
! 		/* stop at chunk boundary */
! 		if (collected >= cm_chunkSize) break;
                  
          /* see if the next page would be past EOF */
          if (LargeIntegerGreaterThanOrEqualTo(pageBase, fileSize)) break;
  
! 		lock_ObtainMutex(&cm_bufGetMutex);
  
! 		code = buf_Get(scp, &pageBase, &tbp);
          if (code) {
! 			lock_ReleaseMutex(&cm_bufGetMutex);
! 			lock_ObtainMutex(&scp->mx);
! 			return code;
! 		}
                  
! 		buf_Release(tbp);
  
! 		lock_ReleaseMutex(&cm_bufGetMutex);
  
          toffset.HighPart = 0;
          toffset.LowPart = buf_bufferSize;
          pageBase = LargeIntegerAdd(toffset, pageBase);
! 		collected += buf_bufferSize;
      }
  
      /* reserve a chunk's worth of buffers if possible */
! 	reserving = buf_TryReserveBuffers(cm_chunkSize / buf_bufferSize);
  
! 	pageBase = *offsetp;
      collected = pageBase.LowPart & (cm_chunkSize - 1);
  
! 	/* now hold all buffers, if they are still there */
      while (1) {
! 		/* stop at chunk boundary */
! 		if (collected >= cm_chunkSize) 
              break;
                  
          /* see if the next page would be past EOF */
--- 833,918 ----
      osi_hyper_t fileSize;		/* the # of bytes in the file */
      osi_queueData_t *heldBufListp;	/* we hold all buffers in this list */
      osi_queueData_t *heldBufListEndp;	/* first one */
!     int reserving;
  
      biop->scp = scp;
      biop->offset = *offsetp;
!     /* null out the list of buffers */
      biop->bufListp = biop->bufListEndp = NULL;
!     biop->reserved = 0;
  
!     /* first lookup the file's length, so we know when to stop */
      code = cm_SyncOp(scp, NULL, up, reqp, 0, 
                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
      if (code) 
          return code;
          
!     /* copy out size, since it may change */
      fileSize = scp->serverLength;
          
      lock_ReleaseMutex(&scp->mx);
  
!     pageBase = *offsetp;
      collected = pageBase.LowPart & (cm_chunkSize - 1);
      heldBufListp = NULL;
      heldBufListEndp = NULL;
  
!     /*
!      * Obtaining buffers can cause dirty buffers to be recycled, which
!      * can cause a storeback, so cannot be done while we have buffers
!      * reserved.
!      *
!      * To get around this, we get buffers twice.  Before reserving buffers,
!      * we obtain and release each one individually.  After reserving
!      * buffers, we try to obtain them again, but only by lookup, not by
!      * recycling.  If a buffer has gone away while we were waiting for
!      * the others, we just use whatever buffers we already have.
!      *
!      * On entry to this function, we are already holding a buffer, so we
!      * can't wait for reservation.  So we call buf_TryReserveBuffers()
!      * instead.  Not only that, we can't really even call buf_Get(), for
!      * the same reason.  We can't avoid that, though.  To avoid deadlock
!      * we allow only one thread to be executing the buf_Get()-buf_Release()
!      * sequence at a time.
!      */
  
!     /* first hold all buffers, since we can't hold any locks in buf_Get */
      while (1) {
!         /* stop at chunk boundary */
!         if (collected >= cm_chunkSize) break;
                  
          /* see if the next page would be past EOF */
          if (LargeIntegerGreaterThanOrEqualTo(pageBase, fileSize)) break;
  
!         lock_ObtainMutex(&cm_bufGetMutex);
  
!         code = buf_Get(scp, &pageBase, &tbp);
          if (code) {
!             lock_ReleaseMutex(&cm_bufGetMutex);
!             lock_ObtainMutex(&scp->mx);
!             return code;
!         }
                  
!         buf_Release(tbp);
  
!         lock_ReleaseMutex(&cm_bufGetMutex);
  
          toffset.HighPart = 0;
          toffset.LowPart = buf_bufferSize;
          pageBase = LargeIntegerAdd(toffset, pageBase);
!         collected += buf_bufferSize;
      }
  
      /* reserve a chunk's worth of buffers if possible */
!     reserving = buf_TryReserveBuffers(cm_chunkSize / buf_bufferSize);
  
!     pageBase = *offsetp;
      collected = pageBase.LowPart & (cm_chunkSize - 1);
  
!     /* now hold all buffers, if they are still there */
      while (1) {
!         /* stop at chunk boundary */
!         if (collected >= cm_chunkSize) 
              break;
                  
          /* see if the next page would be past EOF */
***************
*** 915,931 ****
              break;
  
          tbp = buf_Find(scp, &pageBase);
! 		if (!tbp) 
              break;
  
          /* add the buffer to the list */
! 		qdp = osi_QDAlloc();
          osi_SetQData(qdp, tbp);
          osi_QAdd((osi_queue_t **)&heldBufListp, &qdp->q);
          if (!heldBufListEndp) heldBufListEndp = qdp;
! 		/* leave tbp held (from buf_Get) */
  
! 		if (!reserving) 
              break;
  
          collected += buf_bufferSize;
--- 920,936 ----
              break;
  
          tbp = buf_Find(scp, &pageBase);
!         if (!tbp) 
              break;
  
          /* add the buffer to the list */
!         qdp = osi_QDAlloc();
          osi_SetQData(qdp, tbp);
          osi_QAdd((osi_queue_t **)&heldBufListp, &qdp->q);
          if (!heldBufListEndp) heldBufListEndp = qdp;
!         /* leave tbp held (from buf_Get) */
  
!         if (!reserving) 
              break;
  
          collected += buf_bufferSize;
***************
*** 935,1001 ****
      }
  
      /* look at each buffer, adding it into the list if it looks idle and
! 	 * filled with old data.  One special case: wait for idle if it is the
! 	 * first buffer since we really need that one for our caller to make
! 	 * any progress.
       */
      isFirst = 1;
      collected = 0;		/* now count how many we'll really use */
! 	for(tqdp = heldBufListEndp;
          tqdp;
! 	    tqdp = (osi_queueData_t *) osi_QPrev(&tqdp->q)) {
! 		/* get a ptr to the held buffer */
! 		tbp = osi_GetQData(tqdp);
          pageBase = tbp->offset;
  
! 		/* now lock the buffer lock */
! 		lock_ObtainMutex(&tbp->mx);
! 		lock_ObtainMutex(&scp->mx);
! 
! 		/* don't bother fetching over data that is already current */
! 		if (tbp->dataVersion == scp->dataVersion) {
! 			/* we don't need this buffer, since it is current */
! 			lock_ReleaseMutex(&scp->mx);
              lock_ReleaseMutex(&tbp->mx);
              break;
          }
  
! 		flags = CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_FETCHDATA
!                 	| CM_SCACHESYNC_BUFLOCKED;
! 		if (!isFirst) 
              flags |= CM_SCACHESYNC_NOWAIT;
  
! 		/* wait for the buffer to serialize, if required.  Doesn't
! 		 * release the scp or buffer lock(s) if NOWAIT is specified.
           */
! 		code = cm_SyncOp(scp, tbp, up, reqp, 0, flags);
          if (code) {
! 			lock_ReleaseMutex(&scp->mx);
! 			lock_ReleaseMutex(&tbp->mx);
              break;
! 		}
                  
! 		/* don't fetch over dirty buffers */
          if (tbp->flags & CM_BUF_DIRTY) {
! 			cm_SyncOpDone(scp, tbp, flags);
! 			lock_ReleaseMutex(&scp->mx);
              lock_ReleaseMutex(&tbp->mx);
              break;
! 		}
  
! 		/* Release locks */
! 		lock_ReleaseMutex(&scp->mx);
! 		lock_ReleaseMutex(&tbp->mx);
  
          /* add the buffer to the list */
! 		qdp = osi_QDAlloc();
          osi_SetQData(qdp, tbp);
          osi_QAdd((osi_queue_t **)&biop->bufListp, &qdp->q);
          if (!biop->bufListEndp) 
              biop->bufListEndp = qdp;
! 		buf_Hold(tbp);
  
! 		/* from now on, a failure just stops our collection process, but
           * we still do the I/O to whatever we've already managed to collect.
           */
          isFirst = 0;
--- 940,1006 ----
      }
  
      /* look at each buffer, adding it into the list if it looks idle and
!      * filled with old data.  One special case: wait for idle if it is the
!      * first buffer since we really need that one for our caller to make
!      * any progress.
       */
      isFirst = 1;
      collected = 0;		/* now count how many we'll really use */
!     for (tqdp = heldBufListEndp;
          tqdp;
!           tqdp = (osi_queueData_t *) osi_QPrev(&tqdp->q)) {
!         /* get a ptr to the held buffer */
!         tbp = osi_GetQData(tqdp);
          pageBase = tbp->offset;
  
!         /* now lock the buffer lock */
!         lock_ObtainMutex(&tbp->mx);
!         lock_ObtainMutex(&scp->mx);
! 
!         /* don't bother fetching over data that is already current */
!         if (tbp->dataVersion == scp->dataVersion) {
!             /* we don't need this buffer, since it is current */
!             lock_ReleaseMutex(&scp->mx);
              lock_ReleaseMutex(&tbp->mx);
              break;
          }
  
!         flags = CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_FETCHDATA
!             | CM_SCACHESYNC_BUFLOCKED;
!         if (!isFirst) 
              flags |= CM_SCACHESYNC_NOWAIT;
  
!         /* wait for the buffer to serialize, if required.  Doesn't
!          * release the scp or buffer lock(s) if NOWAIT is specified.
           */
!         code = cm_SyncOp(scp, tbp, up, reqp, 0, flags);
          if (code) {
!             lock_ReleaseMutex(&scp->mx);
!             lock_ReleaseMutex(&tbp->mx);
              break;
!         }
                  
!         /* don't fetch over dirty buffers */
          if (tbp->flags & CM_BUF_DIRTY) {
!             cm_SyncOpDone(scp, tbp, flags);
!             lock_ReleaseMutex(&scp->mx);
              lock_ReleaseMutex(&tbp->mx);
              break;
!         }
  
!         /* Release locks */
!         lock_ReleaseMutex(&scp->mx);
!         lock_ReleaseMutex(&tbp->mx);
  
          /* add the buffer to the list */
!         qdp = osi_QDAlloc();
          osi_SetQData(qdp, tbp);
          osi_QAdd((osi_queue_t **)&biop->bufListp, &qdp->q);
          if (!biop->bufListEndp) 
              biop->bufListEndp = qdp;
!         buf_Hold(tbp);
  
!         /* from now on, a failure just stops our collection process, but
           * we still do the I/O to whatever we've already managed to collect.
           */
          isFirst = 0;
***************
*** 1003,1035 ****
      }
          
      /* now, we've held in biop->bufListp all the buffer's we're really
! 	 * interested in.  We also have holds left from heldBufListp, and we
! 	 * now release those holds on the buffers.
       */
! 	for(qdp = heldBufListp; qdp; qdp = tqdp) {
! 		tqdp = (osi_queueData_t *) osi_QNext(&qdp->q);
! 		tbp = osi_GetQData(qdp);
          osi_QDFree(qdp);
          buf_Release(tbp);
      }
  
! 	/* Caller expects this */
! 	lock_ObtainMutex(&scp->mx);
   
! 	/* if we got a failure setting up the first buffer, then we don't have
       * any side effects yet, and we also have failed an operation that the
       * caller requires to make any progress.  Give up now.
       */
      if (code && isFirst) {
! 		buf_UnreserveBuffers(cm_chunkSize / buf_bufferSize);
! 		return code;
! 	}
          
      /* otherwise, we're still OK, and should just return the I/O setup we've
       * got.
       */
! 	biop->length = collected;
! 	biop->reserved = reserving;
      return 0;
  }
  
--- 1008,1040 ----
      }
          
      /* now, we've held in biop->bufListp all the buffer's we're really
!      * interested in.  We also have holds left from heldBufListp, and we
!      * now release those holds on the buffers.
       */
!     for (qdp = heldBufListp; qdp; qdp = tqdp) {
!         tqdp = (osi_queueData_t *) osi_QNext(&qdp->q);
!         tbp = osi_GetQData(qdp);
          osi_QDFree(qdp);
          buf_Release(tbp);
      }
  
!     /* Caller expects this */
!     lock_ObtainMutex(&scp->mx);
   
!     /* if we got a failure setting up the first buffer, then we don't have
       * any side effects yet, and we also have failed an operation that the
       * caller requires to make any progress.  Give up now.
       */
      if (code && isFirst) {
!         buf_UnreserveBuffers(cm_chunkSize / buf_bufferSize);
!         return code;
!     }
          
      /* otherwise, we're still OK, and should just return the I/O setup we've
       * got.
       */
!     biop->length = collected;
!     biop->reserved = reserving;
      return 0;
  }
  
***************
*** 1038,1078 ****
   */
  void cm_ReleaseBIOD(cm_bulkIO_t *biop, int isStore)
  {
! 	cm_scache_t *scp;
      cm_buf_t *bufp;
      osi_queueData_t *qdp;
      osi_queueData_t *nqdp;
      int flags;
  
! 	/* Give back reserved buffers */
! 	if (biop->reserved)
! 		buf_UnreserveBuffers(cm_chunkSize / buf_bufferSize);
          
! 	flags = CM_SCACHESYNC_NEEDCALLBACK;
      if (isStore)
          flags |= CM_SCACHESYNC_STOREDATA;
! 	else
! 		flags |= CM_SCACHESYNC_FETCHDATA;
  
! 	scp = biop->scp;
      for(qdp = biop->bufListp; qdp; qdp = nqdp) {
! 		/* lookup next guy first, since we're going to free this one */
! 		nqdp = (osi_queueData_t *) osi_QNext(&qdp->q);
                  
! 		/* extract buffer and free queue data */
          bufp = osi_GetQData(qdp);
          osi_QDFree(qdp);
  
          /* now, mark I/O as done, unlock the buffer and release it */
! 		lock_ObtainMutex(&bufp->mx);
! 		lock_ObtainMutex(&scp->mx);
          cm_SyncOpDone(scp, bufp, flags);
! 		lock_ReleaseMutex(&scp->mx);
                  
! 		/* turn off writing and wakeup users */
          if (isStore) {
              if (bufp->flags & CM_BUF_WAITING) {
! 				osi_Wakeup((long) bufp);
              }
              bufp->flags &= ~(CM_BUF_WAITING | CM_BUF_WRITING | CM_BUF_DIRTY);
          }
--- 1043,1083 ----
   */
  void cm_ReleaseBIOD(cm_bulkIO_t *biop, int isStore)
  {
!     cm_scache_t *scp;
      cm_buf_t *bufp;
      osi_queueData_t *qdp;
      osi_queueData_t *nqdp;
      int flags;
  
!     /* Give back reserved buffers */
!     if (biop->reserved)
!         buf_UnreserveBuffers(cm_chunkSize / buf_bufferSize);
          
!     flags = CM_SCACHESYNC_NEEDCALLBACK;
      if (isStore)
          flags |= CM_SCACHESYNC_STOREDATA;
!     else
!         flags |= CM_SCACHESYNC_FETCHDATA;
  
!     scp = biop->scp;
      for(qdp = biop->bufListp; qdp; qdp = nqdp) {
!         /* lookup next guy first, since we're going to free this one */
!         nqdp = (osi_queueData_t *) osi_QNext(&qdp->q);
                  
!         /* extract buffer and free queue data */
          bufp = osi_GetQData(qdp);
          osi_QDFree(qdp);
  
          /* now, mark I/O as done, unlock the buffer and release it */
!         lock_ObtainMutex(&bufp->mx);
!         lock_ObtainMutex(&scp->mx);
          cm_SyncOpDone(scp, bufp, flags);
!         lock_ReleaseMutex(&scp->mx);
                  
!         /* turn off writing and wakeup users */
          if (isStore) {
              if (bufp->flags & CM_BUF_WAITING) {
!                 osi_Wakeup((long) bufp);
              }
              bufp->flags &= ~(CM_BUF_WAITING | CM_BUF_WRITING | CM_BUF_DIRTY);
          }
***************
*** 1090,1098 ****
   * The scp is locked on return.
   */
  long cm_GetBuffer(cm_scache_t *scp, cm_buf_t *bufp, int *cpffp, cm_user_t *up,
! 	cm_req_t *reqp)
  {
! 	long code;
      long nbytes;			/* bytes in transfer */
      long rbytes;			/* bytes in rx_Read call */
      long temp;
--- 1095,1103 ----
   * The scp is locked on return.
   */
  long cm_GetBuffer(cm_scache_t *scp, cm_buf_t *bufp, int *cpffp, cm_user_t *up,
!                   cm_req_t *reqp)
  {
!     long code;
      long nbytes;			/* bytes in transfer */
      long rbytes;			/* bytes in rx_Read call */
      long temp;
***************
*** 1106,1113 ****
      struct rx_call *callp;
      cm_bulkIO_t biod;		/* bulk IO descriptor */
      cm_conn_t *connp;
! 	int getroot;
! 	long t1, t2;
  
      /* now, the buffer may or may not be filled with good data (buf_GetNew
       * drops lots of locks, and may indeed return a properly initialized
--- 1111,1118 ----
      struct rx_call *callp;
      cm_bulkIO_t biod;		/* bulk IO descriptor */
      cm_conn_t *connp;
!     int getroot;
!     long t1, t2;
  
      /* now, the buffer may or may not be filled with good data (buf_GetNew
       * drops lots of locks, and may indeed return a properly initialized
***************
*** 1116,1160 ****
  
  #ifdef AFS_FREELANCE_CLIENT
  
! 	// yj: if they're trying to get the /afs directory, we need to
! 	// handle it differently, since it's local rather than on any
! 	// server
! 
! 	getroot = (scp==cm_rootSCachep);
! 	if (getroot)
! 		osi_Log1(afsd_logp,"GetBuffer returns cm_rootSCachep=%x",cm_rootSCachep);
  #endif
  
! 	cm_AFSFidFromFid(&tfid, &scp->fid);
  
! 	code = cm_SetupFetchBIOD(scp, &bufp->offset, &biod, up, reqp);
! 	if (code) {
! 		/* couldn't even get the first page setup properly */
! 		osi_Log1(afsd_logp, "SetupFetchBIOD failure code %d", code);
          return code;
! 	}
  
      /* once we get here, we have the callback in place, we know that no one
! 	 * is fetching the data now.  Check one last time that we still have
! 	 * the wrong data, and then fetch it if we're still wrong.
! 	 *
       * We can lose a race condition and end up with biod.length zero, in
! 	 * which case we just retry.
       */
      if (bufp->dataVersion == scp->dataVersion || biod.length == 0) {
! 		osi_Log3(afsd_logp, "Bad DVs %d, %d or length 0x%x",
                   bufp->dataVersion, scp->dataVersion, biod.length);
! 		if ((bufp->dataVersion == -1
! 		     || bufp->dataVersion < scp->dataVersion)
               && LargeIntegerGreaterThanOrEqualTo(bufp->offset,
                                                   scp->serverLength)) {
! 			if (bufp->dataVersion == -1)
! 				memset(bufp->datap, 0, buf_bufferSize);
! 			bufp->dataVersion = scp->dataVersion;
! 		}
! 		lock_ReleaseMutex(&scp->mx);
! 		cm_ReleaseBIOD(&biod, 0);
! 		lock_ObtainMutex(&scp->mx);
          return 0;
      }
          
--- 1121,1165 ----
  
  #ifdef AFS_FREELANCE_CLIENT
  
!     // yj: if they're trying to get the /afs directory, we need to
!     // handle it differently, since it's local rather than on any
!     // server
! 
!     getroot = (scp==cm_rootSCachep);
!     if (getroot)
!         osi_Log1(afsd_logp,"GetBuffer returns cm_rootSCachep=%x",cm_rootSCachep);
  #endif
  
!     cm_AFSFidFromFid(&tfid, &scp->fid);
  
!     code = cm_SetupFetchBIOD(scp, &bufp->offset, &biod, up, reqp);
!     if (code) {
!         /* couldn't even get the first page setup properly */
!         osi_Log1(afsd_logp, "SetupFetchBIOD failure code %d", code);
          return code;
!     }
  
      /* once we get here, we have the callback in place, we know that no one
!      * is fetching the data now.  Check one last time that we still have
!      * the wrong data, and then fetch it if we're still wrong.
!      *
       * We can lose a race condition and end up with biod.length zero, in
!      * which case we just retry.
       */
      if (bufp->dataVersion == scp->dataVersion || biod.length == 0) {
!         osi_Log3(afsd_logp, "Bad DVs %d, %d or length 0x%x",
                   bufp->dataVersion, scp->dataVersion, biod.length);
!         if ((bufp->dataVersion == -1
!              || bufp->dataVersion < scp->dataVersion)
               && LargeIntegerGreaterThanOrEqualTo(bufp->offset,
                                                   scp->serverLength)) {
!             if (bufp->dataVersion == -1)
!                 memset(bufp->datap, 0, buf_bufferSize);
!             bufp->dataVersion = scp->dataVersion;
!         }
!         lock_ReleaseMutex(&scp->mx);
!         cm_ReleaseBIOD(&biod, 0);
!         lock_ObtainMutex(&scp->mx);
          return 0;
      }
          
***************
*** 1167,1366 ****
  
  #ifdef AFS_FREELANCE_CLIENT
  
! 	// yj code
! 	// if getroot then we don't need to make any calls
! 	// just return fake data
  	
! 	 if (cm_freelanceEnabled && getroot) {
! 		// setup the fake status			
! 		afsStatus.InterfaceVersion = 0x1;
! 		afsStatus.FileType = 0x2;
! 		afsStatus.LinkCount = scp->linkCount;
! 		afsStatus.Length = cm_fakeDirSize;
! 		afsStatus.DataVersion = cm_fakeDirVersion;
! 		afsStatus.Author = 0x1;
! 		afsStatus.Owner = 0x0;
! 		afsStatus.CallerAccess = 0x9;
! 		afsStatus.AnonymousAccess = 0x9;
! 		afsStatus.UnixModeBits = 0x1ff;
! 		afsStatus.ParentVnode = 0x1;
! 		afsStatus.ParentUnique = 0x1;
! 		afsStatus.ResidencyMask = 0;
! 		afsStatus.ClientModTime = FakeFreelanceModTime;
! 		afsStatus.ServerModTime = FakeFreelanceModTime;
! 		afsStatus.Group = 0;
! 		afsStatus.SyncCounter = 0;
! 		afsStatus.dataVersionHigh = 0;
  	
! 		// once we're done setting up the status info,
! 		// we just fill the buffer pages with fakedata
! 		// from cm_FakeRootDir. Extra pages are set to
! 		// 0. 
  		
! 		lock_ObtainMutex(&cm_Freelance_Lock);
! 		t1 = bufp->offset.LowPart;
! 		qdp = biod.bufListEndp;
! 		while (qdp) {
! 			tbufp = osi_GetQData(qdp);
! 			bufferp=tbufp->datap;
! 			memset(bufferp, 0, buf_bufferSize);
! 			t2 = cm_fakeDirSize - t1;
! 			if (t2>buf_bufferSize) t2=buf_bufferSize;
! 			if (t2 > 0) {
! 				memcpy(bufferp, cm_FakeRootDir+t1, t2);
! 			} else {
! 				t2 = 0;
! 			}
! 			t1+=t2;
! 			qdp = (osi_queueData_t *) osi_QPrev(&qdp->q);
! 			
! 		}
! 		lock_ReleaseMutex(&cm_Freelance_Lock);
  	
! 		// once we're done, we skip over the part of the
! 		// code that does the ACTUAL fetching of data for
! 		// real files
  
! 		goto fetchingcompleted;
! 	}
  
  #endif /* AFS_FREELANCE_CLIENT */
  
  	/* now make the call */
      do {
! 		code = cm_Conn(&scp->fid, up, reqp, &connp);
          if (code) 
              continue;
  		
! 		callp = rx_NewCall(connp->callp);
  
! 		osi_Log3(afsd_logp, "CALL FetchData vp %x, off 0x%x, size 0x%x",
!                  (long) scp, biod.offset.LowPart, biod.length);
  
          code = StartRXAFS_FetchData(callp, &tfid, biod.offset.LowPart,
                                      biod.length);
  
! 		/* now copy the data out of the pipe and put it in the buffer */
! 		temp  = rx_Read(callp, (char *)&nbytes, 4);
! 		if (temp == 4) {
! 			nbytes = ntohl(nbytes);
              if (nbytes > biod.length) 
                  code = (callp->error < 0) ? callp->error : -1;
          }
          else 
              code = (callp->error < 0) ? callp->error : -1;
  
! 		if (code == 0) {
              qdp = biod.bufListEndp;
              if (qdp) {
! 				tbufp = osi_GetQData(qdp);
                  bufferp = tbufp->datap;
              }
              else 
                  bufferp = NULL;
! 			/* fill nbytes of data from the pipe into the pages.
! 			 * When we stop, qdp will point at the last page we're
! 			 * dealing with, and bufferp will tell us where we
! 			 * stopped.  We'll need this info below when we clear
! 			 * the remainder of the last page out (and potentially
               * clear later pages out, if we fetch past EOF).
               */
!             while(nbytes > 0) {
! 				/* assert that there are still more buffers;
! 				 * our check above for nbytes being less than
! 				 * biod.length should ensure this.
                   */
! 				osi_assert(bufferp != NULL);
  
! 				/* read rbytes of data */
                  rbytes = (nbytes > buf_bufferSize? buf_bufferSize : nbytes);
                  temp = rx_Read(callp, bufferp, rbytes);
                  if (temp < rbytes) {
                      code = (callp->error < 0) ? callp->error : -1;
                      break;
! 				}
  
! 				/* allow read-while-fetching.
! 				 * if this is the last buffer, clear the
! 				 * PREFETCHING flag, so the reader waiting for
! 				 * this buffer will start a prefetch.
! 				 */
! 				tbufp->cmFlags |= CM_BUF_CMFULLYFETCHED;
! 				lock_ObtainMutex(&scp->mx);
! 				if (scp->flags & CM_SCACHEFLAG_WAITING) {
! 					scp->flags &= ~CM_SCACHEFLAG_WAITING;
! 					osi_Wakeup((long) &scp->flags);
! 				}
! 				if (cpffp && !*cpffp && !osi_QPrev(&qdp->q)) {
! 					*cpffp = 1;
! 					cm_ClearPrefetchFlag(0, scp, &biod.offset);
! 				}
! 				lock_ReleaseMutex(&scp->mx);
  
! 				/* and adjust counters */
                  nbytes -= temp;
!                                 
                  /* and move to the next buffer */
! 				if (nbytes != 0) {
                      qdp = (osi_queueData_t *) osi_QPrev(&qdp->q);
                      if (qdp) {
! 						tbufp = osi_GetQData(qdp);
                          bufferp = tbufp->datap;
                      }
                      else 
                          bufferp = NULL;
! 				} else 
                      bufferp += temp;
              }
  
              /* zero out remainder of last pages, in case we are
! 			 * fetching past EOF.  We were fetching an integral #
! 			 * of pages, but stopped, potentially in the middle of
! 			 * a page.  Zero the remainder of that page, and then
! 			 * all of the rest of the pages.
               */
! 			/* bytes fetched */
              rbytes = bufferp - tbufp->datap;
! 			/* bytes left to zero */
              rbytes = buf_bufferSize - rbytes;
              while(qdp) {
                  if (rbytes != 0)
! 					memset(bufferp, 0, rbytes);
                  qdp = (osi_queueData_t *) osi_QPrev(&qdp->q);
! 				if (qdp == NULL) 
                      break;
! 				tbufp = osi_GetQData(qdp);
                  bufferp = tbufp->datap;
! 				/* bytes to clear in this page */
! 				rbytes = buf_bufferSize;
!             }
! 		}
  
! 		if (code == 0)
! 			code = EndRXAFS_FetchData(callp, &afsStatus, &callback, &volSync);
! 		else
! 			osi_Log0(afsd_logp, "CALL EndRXAFS_FetchData skipped due to error");
          code = rx_EndCall(callp, code);
          if (code == RXKADUNKNOWNKEY)
              osi_Log0(afsd_logp, "CALL EndCall returns RXKADUNKNOWNKEY");
          osi_Log0(afsd_logp, "CALL FetchData DONE");
  
! 	} while (cm_Analyze(connp, up, reqp, &scp->fid, &volSync, NULL, NULL, code));
  
    fetchingcompleted:
      code = cm_MapRPCError(code, reqp);
  
      lock_ObtainMutex(&scp->mx);
! 	/* we know that no one else has changed the buffer, since we still have
! 	 * the fetching flag on the buffers, and we have the scp locked again.
! 	 * Copy in the version # into the buffer if we got code 0 back from the
! 	 * read.
       */
! 	if (code == 0) {
! 		for(qdp = biod.bufListp;
! 		    qdp;
! 		    qdp = (osi_queueData_t *) osi_QNext(&qdp->q)) {
! 			tbufp = osi_GetQData(qdp);
              tbufp->dataVersion = afsStatus.DataVersion;
  
  #ifdef DISKCACHE95
--- 1172,1373 ----
  
  #ifdef AFS_FREELANCE_CLIENT
  
!     // yj code
!     // if getroot then we don't need to make any calls
!     // just return fake data
  	
!     if (cm_freelanceEnabled && getroot) {
!         // setup the fake status			
!         afsStatus.InterfaceVersion = 0x1;
!         afsStatus.FileType = 0x2;
!         afsStatus.LinkCount = scp->linkCount;
!         afsStatus.Length = cm_fakeDirSize;
!         afsStatus.DataVersion = cm_fakeDirVersion;
!         afsStatus.Author = 0x1;
!         afsStatus.Owner = 0x0;
!         afsStatus.CallerAccess = 0x9;
!         afsStatus.AnonymousAccess = 0x9;
!         afsStatus.UnixModeBits = 0x1ff;
!         afsStatus.ParentVnode = 0x1;
!         afsStatus.ParentUnique = 0x1;
!         afsStatus.ResidencyMask = 0;
!         afsStatus.ClientModTime = FakeFreelanceModTime;
!         afsStatus.ServerModTime = FakeFreelanceModTime;
!         afsStatus.Group = 0;
!         afsStatus.SyncCounter = 0;
!         afsStatus.dataVersionHigh = 0;
  	
!         // once we're done setting up the status info,
!         // we just fill the buffer pages with fakedata
!         // from cm_FakeRootDir. Extra pages are set to
!         // 0. 
  		
!         lock_ObtainMutex(&cm_Freelance_Lock);
!         t1 = bufp->offset.LowPart;
!         qdp = biod.bufListEndp;
!         while (qdp) {
!             tbufp = osi_GetQData(qdp);
!             bufferp=tbufp->datap;
!             memset(bufferp, 0, buf_bufferSize);
!             t2 = cm_fakeDirSize - t1;
!             if (t2>buf_bufferSize) t2=buf_bufferSize;
!             if (t2 > 0) {
!                 memcpy(bufferp, cm_FakeRootDir+t1, t2);
!             } else {
!                 t2 = 0;
!             }
!             t1+=t2;
!             qdp = (osi_queueData_t *) osi_QPrev(&qdp->q);
! 
!         }
!         lock_ReleaseMutex(&cm_Freelance_Lock);
  	
!         // once we're done, we skip over the part of the
!         // code that does the ACTUAL fetching of data for
!         // real files
  
!         goto fetchingcompleted;
!     }
  
  #endif /* AFS_FREELANCE_CLIENT */
  
  	/* now make the call */
      do {
!         code = cm_Conn(&scp->fid, up, reqp, &connp);
          if (code) 
              continue;
  		
!         lock_ObtainMutex(&connp->mx);
!         callp = rx_NewCall(connp->callp);
!         lock_ReleaseMutex(&connp->mx);
  
!         osi_Log3(afsd_logp, "CALL FetchData vp %x, off 0x%x, size 0x%x",
!                   (long) scp, biod.offset.LowPart, biod.length);
  
          code = StartRXAFS_FetchData(callp, &tfid, biod.offset.LowPart,
                                      biod.length);
  
!         /* now copy the data out of the pipe and put it in the buffer */
!         temp  = rx_Read(callp, (char *)&nbytes, 4);
!         if (temp == 4) {
!             nbytes = ntohl(nbytes);
              if (nbytes > biod.length) 
                  code = (callp->error < 0) ? callp->error : -1;
          }
          else 
              code = (callp->error < 0) ? callp->error : -1;
  
!         if (code == 0) {
              qdp = biod.bufListEndp;
              if (qdp) {
!                 tbufp = osi_GetQData(qdp);
                  bufferp = tbufp->datap;
              }
              else 
                  bufferp = NULL;
!             /* fill nbytes of data from the pipe into the pages.
!              * When we stop, qdp will point at the last page we're
!              * dealing with, and bufferp will tell us where we
!              * stopped.  We'll need this info below when we clear
!              * the remainder of the last page out (and potentially
               * clear later pages out, if we fetch past EOF).
               */
!             while (nbytes > 0) {
!                 /* assert that there are still more buffers;
!                  * our check above for nbytes being less than
!                  * biod.length should ensure this.
                   */
!                 osi_assert(bufferp != NULL);
  
!                 /* read rbytes of data */
                  rbytes = (nbytes > buf_bufferSize? buf_bufferSize : nbytes);
                  temp = rx_Read(callp, bufferp, rbytes);
                  if (temp < rbytes) {
                      code = (callp->error < 0) ? callp->error : -1;
                      break;
!                 }
  
!                 /* allow read-while-fetching.
!                  * if this is the last buffer, clear the
!                  * PREFETCHING flag, so the reader waiting for
!                  * this buffer will start a prefetch.
!                  */
!                 tbufp->cmFlags |= CM_BUF_CMFULLYFETCHED;
!                 lock_ObtainMutex(&scp->mx);
!                 if (scp->flags & CM_SCACHEFLAG_WAITING) {
!                     scp->flags &= ~CM_SCACHEFLAG_WAITING;
!                     osi_Wakeup((long) &scp->flags);
!                 }
!                 if (cpffp && !*cpffp && !osi_QPrev(&qdp->q)) {
!                     *cpffp = 1;
!                     cm_ClearPrefetchFlag(0, scp, &biod.offset);
!                 }
!                 lock_ReleaseMutex(&scp->mx);
  
!                 /* and adjust counters */
                  nbytes -= temp;
! 
                  /* and move to the next buffer */
!                 if (nbytes != 0) {
                      qdp = (osi_queueData_t *) osi_QPrev(&qdp->q);
                      if (qdp) {
!                         tbufp = osi_GetQData(qdp);
                          bufferp = tbufp->datap;
                      }
                      else 
                          bufferp = NULL;
!                 } else 
                      bufferp += temp;
              }
  
              /* zero out remainder of last pages, in case we are
!              * fetching past EOF.  We were fetching an integral #
!              * of pages, but stopped, potentially in the middle of
!              * a page.  Zero the remainder of that page, and then
!              * all of the rest of the pages.
               */
!             /* bytes fetched */
              rbytes = bufferp - tbufp->datap;
!             /* bytes left to zero */
              rbytes = buf_bufferSize - rbytes;
              while(qdp) {
                  if (rbytes != 0)
!                     memset(bufferp, 0, rbytes);
                  qdp = (osi_queueData_t *) osi_QPrev(&qdp->q);
!                 if (qdp == NULL) 
                      break;
!                 tbufp = osi_GetQData(qdp);
                  bufferp = tbufp->datap;
!                 /* bytes to clear in this page */
!                 rbytes = buf_bufferSize;
!             }   
!         }
  
!         if (code == 0)
!             code = EndRXAFS_FetchData(callp, &afsStatus, &callback, &volSync);
!         else
!             osi_Log0(afsd_logp, "CALL EndRXAFS_FetchData skipped due to error");
          code = rx_EndCall(callp, code);
          if (code == RXKADUNKNOWNKEY)
              osi_Log0(afsd_logp, "CALL EndCall returns RXKADUNKNOWNKEY");
          osi_Log0(afsd_logp, "CALL FetchData DONE");
  
!     } while (cm_Analyze(connp, up, reqp, &scp->fid, &volSync, NULL, NULL, code));
  
    fetchingcompleted:
      code = cm_MapRPCError(code, reqp);
  
      lock_ObtainMutex(&scp->mx);
!     /* we know that no one else has changed the buffer, since we still have
!      * the fetching flag on the buffers, and we have the scp locked again.
!      * Copy in the version # into the buffer if we got code 0 back from the
!      * read.
       */
!     if (code == 0) {
!         for(qdp = biod.bufListp;
!              qdp;
!              qdp = (osi_queueData_t *) osi_QNext(&qdp->q)) {
!             tbufp = osi_GetQData(qdp);
              tbufp->dataVersion = afsStatus.DataVersion;
  
  #ifdef DISKCACHE95
***************
*** 1371,1382 ****
          }
      }
  
! 	/* release scatter/gather I/O structure (buffers, locks) */
! 	lock_ReleaseMutex(&scp->mx);
! 	cm_ReleaseBIOD(&biod, 0);
! 	lock_ObtainMutex(&scp->mx);
  
      if (code == 0) 
          cm_MergeStatus(scp, &afsStatus, &volSync, up, 0);
! 	return code;
  }
--- 1378,1389 ----
          }
      }
  
!     /* release scatter/gather I/O structure (buffers, locks) */
!     lock_ReleaseMutex(&scp->mx);
!     cm_ReleaseBIOD(&biod, 0);
!     lock_ObtainMutex(&scp->mx);
  
      if (code == 0) 
          cm_MergeStatus(scp, &afsStatus, &volSync, up, 0);
!     return code;
  }
Index: openafs/src/WINNT/afsd/cm_diskcache95.h
diff -c openafs/src/WINNT/afsd/cm_diskcache95.h:1.1 openafs/src/WINNT/afsd/cm_diskcache95.h:1.1.18.1
*** openafs/src/WINNT/afsd/cm_diskcache95.h:1.1	Mon Apr 30 02:48:04 2001
--- openafs/src/WINNT/afsd/cm_diskcache95.h	Mon Oct 18 00:09:26 2004
***************
*** 51,57 ****
    int openfd;      /* open file descriptor */
    struct cm_diskcache *hash_next;
    struct cm_diskcache *hash_prev;
!   int refCount;
    osi_mutex_t mx;
  } cm_diskcache_t;
  
--- 51,57 ----
    int openfd;      /* open file descriptor */
    struct cm_diskcache *hash_next;
    struct cm_diskcache *hash_prev;
!   unsigned long refCount;
    osi_mutex_t mx;
  } cm_diskcache_t;
  
Index: openafs/src/WINNT/afsd/cm_freelance.c
diff -c openafs/src/WINNT/afsd/cm_freelance.c:1.15.2.1 openafs/src/WINNT/afsd/cm_freelance.c:1.15.2.2
*** openafs/src/WINNT/afsd/cm_freelance.c:1.15.2.1	Tue Aug 17 00:28:39 2004
--- openafs/src/WINNT/afsd/cm_freelance.c	Mon Oct 18 00:09:26 2004
***************
*** 30,36 ****
  osi_mutex_t cm_Freelance_Lock;
  int cm_localMountPointChangeFlag = 0;
  int cm_freelanceEnabled = 0;
! afs_uint32    FakeFreelanceModTime = 0x3b49f6e2;
  
  void cm_InitFakeRootDir();
  
--- 30,36 ----
  osi_mutex_t cm_Freelance_Lock;
  int cm_localMountPointChangeFlag = 0;
  int cm_freelanceEnabled = 0;
! time_t FakeFreelanceModTime = 0x3b49f6e2;
  
  void cm_InitFakeRootDir();
  
***************
*** 81,103 ****
      int lpid;
  #endif
  
! 	lock_InitializeMutex(&cm_Freelance_Lock, "Freelance Lock");
  
! 	// yj: first we make a call to cm_initLocalMountPoints
! 	// to read all the local mount points from an ini file
! 	cm_InitLocalMountPoints();
! 
! 	// then we make a call to InitFakeRootDir to create
! 	// a fake root directory based on the local mount points
! 	cm_InitFakeRootDir();
! 	// --- end of yj code
  
  #if !defined(DJGPP)
      /* Start the registry monitor */
      phandle = thrd_Create(NULL, 65536, (ThreadFunc) cm_FreelanceChangeNotifier,
                            NULL, 0, &lpid, "cm_FreelanceChangeNotifier");
! 	osi_assert(phandle != NULL);
! 	thrd_CloseHandle(phandle);
  #endif
  }
  
--- 81,103 ----
      int lpid;
  #endif
  
!     lock_InitializeMutex(&cm_Freelance_Lock, "Freelance Lock");
  
!     // yj: first we make a call to cm_initLocalMountPoints
!     // to read all the local mount points from an ini file
!     cm_InitLocalMountPoints();
! 
!     // then we make a call to InitFakeRootDir to create
!     // a fake root directory based on the local mount points
!     cm_InitFakeRootDir();
!     // --- end of yj code
  
  #if !defined(DJGPP)
      /* Start the registry monitor */
      phandle = thrd_Create(NULL, 65536, (ThreadFunc) cm_FreelanceChangeNotifier,
                            NULL, 0, &lpid, "cm_FreelanceChangeNotifier");
!     osi_assert(phandle != NULL);
!     thrd_CloseHandle(phandle);
  #endif
  }
  
***************
*** 105,290 ****
  /* to be called while holding freelance lock unless during init. */
  void cm_InitFakeRootDir() {
  	
! 	int i, t1, t2;
! 	char* currentPos;
! 	int noChunks;
! 
! 	// allocate space for the fake info
! 	cm_dirHeader_t fakeDirHeader;
! 	cm_dirEntry_t fakeEntry;
! 	cm_pageHeader_t fakePageHeader;
! 
! 	// i'm going to calculate how much space is needed for
! 	// this fake root directory. we have these rules:
! 	// 1. there are cm_noLocalMountPoints number of entries
! 	// 2. each page is CM_DIR_PAGESIZE in size
! 	// 3. the first 13 chunks of the first page are used for
! 	//    some header stuff
! 	// 4. the first chunk of all subsequent pages are used
! 	//    for page header stuff
! 	// 5. a max of CM_DIR_EPP entries are allowed per page
! 	// 6. each entry takes 1 or more chunks, depending on 
! 	//    the size of the mount point string, as determined
! 	//    by cm_NameEntries
! 	// 7. each chunk is CM_DIR_CHUNKSIZE bytes
! 
! 	int CPP = CM_DIR_PAGESIZE / CM_DIR_CHUNKSIZE;
! 	int curChunk = 13;	// chunks 0 - 12 are used for header stuff
! 						// of the first page in the directory
! 	int curPage = 0;
! 	int curDirEntry = 0;
! 	int curDirEntryInPage = 0;
! 	int sizeOfCurEntry;
! 	int dirSize;
! 
! 	/* Reserve 2 directory chunks for "." and ".." */
! 	curChunk += 2;
! 
! 	while (curDirEntry!=cm_noLocalMountPoints) {
! 		sizeOfCurEntry = cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0);
! 		if ((curChunk + sizeOfCurEntry >= CPP) ||
! 			(curDirEntryInPage + 1 >= CM_DIR_EPP)) {
! 			curPage++;
! 			curDirEntryInPage = 0;
! 			curChunk = 1;
! 		}
! 		curChunk += sizeOfCurEntry;
! 		curDirEntry++;
! 		curDirEntryInPage++;
! 	}
! 
! 	dirSize = (curPage+1) *  CM_DIR_PAGESIZE;
! 	cm_FakeRootDir = malloc(dirSize);
! 	cm_fakeDirSize = dirSize;
! 
! 	// yj: when we get here, we've figured out how much memory we need and 
! 	// allocated the appropriate space for it. we now prceed to fill
! 	// it up with entries.
! 	curPage = 0;
! 	curDirEntry = 0;
! 	curDirEntryInPage = 0;
! 	curChunk = 0;
! 	
! 	// fields in the directory entry that are unused.
! 	fakeEntry.flag = 1;
! 	fakeEntry.length = 0;
! 	fakeEntry.next = 0;
! 	fakeEntry.fid.unique = htonl(1);
! 
! 	// the first page is special, it uses fakeDirHeader instead of fakePageHeader
! 	// we fill up the page with dirEntries that belong there and we make changes
! 	// to the fakeDirHeader.header.freeBitmap along the way. Then when we're done
! 	// filling up the dirEntries in this page, we copy the fakeDirHeader into 
! 	// the top of the page.
! 
! 	// init the freeBitmap array
! 	for (i=0; i<8; i++) 
! 		fakeDirHeader.header.freeBitmap[i]=0;
  
! 	fakeDirHeader.header.freeBitmap[0] = 0xff;
! 	fakeDirHeader.header.freeBitmap[1] = 0x7f;
! 	
  
! 	// we start counting at 13 because the 0th to 12th chunks are used for header
! 	curChunk = 13;
  
! 	// stick the first 2 entries "." and ".." in
! 	fakeEntry.fid.unique = htonl(1);
! 	fakeEntry.fid.vnode = htonl(1);
! 	strcpy(fakeEntry.name, ".");
! 	currentPos = cm_FakeRootDir + curPage * CM_DIR_PAGESIZE + curChunk * CM_DIR_CHUNKSIZE;
! 	memcpy(currentPos, &fakeEntry, CM_DIR_CHUNKSIZE);
! 	curChunk++; curDirEntryInPage++;
! 	strcpy(fakeEntry.name, "..");
! 	currentPos = cm_FakeRootDir + curPage * CM_DIR_PAGESIZE + curChunk * CM_DIR_CHUNKSIZE;
! 	memcpy(currentPos, &fakeEntry, CM_DIR_CHUNKSIZE);
! 	curChunk++; curDirEntryInPage++;
! 
! 	// keep putting stuff into page 0 if
! 	// 1. we're not done with all entries
! 	// 2. we have less than CM_DIR_EPP entries in page 0
! 	// 3. we're not out of chunks in page 0
! 
! 	while( (curDirEntry!=cm_noLocalMountPoints) && 
! 		   (curDirEntryInPage < CM_DIR_EPP) &&
! 		   (curChunk + cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0) <= CPP)) 
! 	{
! 
! 		noChunks = cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0);
! 		fakeEntry.fid.vnode = htonl(curDirEntry + 2);
! 		currentPos = cm_FakeRootDir + curPage * CM_DIR_PAGESIZE + curChunk * CM_DIR_CHUNKSIZE;
! 
! 		memcpy(currentPos, &fakeEntry, CM_DIR_CHUNKSIZE);
! 		strcpy(currentPos + 12, (cm_localMountPoints+curDirEntry)->namep);
! 		curDirEntry++;
! 		curDirEntryInPage++;
! 		for (i=0; i<noChunks; i++) {
! 			t1 = (curChunk + i) / 8;
! 			t2 = curChunk + i - (t1*8);
! 			fakeDirHeader.header.freeBitmap[t1] |= (1 << t2);
! 		}
! 		curChunk+=noChunks;
! 	}
! 
! 	// when we get here, we're done with filling in the entries for page 0
! 	// copy in the header info
! 
! 	memcpy(cm_FakeRootDir, &fakeDirHeader, 13 * CM_DIR_CHUNKSIZE);
! 
! 	curPage++;
! 
! 	// ok, page 0's done. Move on to the next page.
! 	while (curDirEntry!=cm_noLocalMountPoints) {
! 		// setup a new page
! 		curChunk = 1;			// the zeroth chunk is reserved for page header
! 		curDirEntryInPage = 0; 
! 		for (i=0; i<8; i++) {
! 			fakePageHeader.freeBitmap[i]=0;
! 		}
! 		fakePageHeader.freeCount = 0;
! 		fakePageHeader.pgcount = 0;
! 		fakePageHeader.tag = htons(1234);
! 		
! 		// while we're on the same page...
! 		while ( (curDirEntry!=cm_noLocalMountPoints) &&
! 				(curDirEntryInPage < CM_DIR_EPP) &&
! 			    (curChunk + cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0) <= CPP))
! 		{
! 			// add an entry to this page
! 
! 			noChunks = cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0);
! 			fakeEntry.fid.vnode=htonl(curDirEntry+2);
! 			currentPos = cm_FakeRootDir + curPage * CM_DIR_PAGESIZE + curChunk * CM_DIR_CHUNKSIZE;
! 			memcpy(currentPos, &fakeEntry, CM_DIR_CHUNKSIZE);
! 			strcpy(currentPos + 12, (cm_localMountPoints+curDirEntry)->namep);
! 			curDirEntry++;
! 			curDirEntryInPage++;
! 			for (i=0; i<noChunks; i++) {
! 				t1 = (curChunk + i) / 8;
! 				t2 = curChunk + i - (t1*8);
! 				fakePageHeader.freeBitmap[t1] |= (1 << t2);
! 			}
! 			curChunk+=noChunks;
! 		}
! 		memcpy(cm_FakeRootDir + curPage * CM_DIR_PAGESIZE, &fakePageHeader, sizeof(fakePageHeader));
  
! 		curPage++;
! 	}
! 	
! 	// we know the fakeDir is setup properly, so we claim that we have callback
      osi_Log0(afsd_logp,"cm_InitFakeRootDir fakeDirCallback=1");
! 	cm_fakeDirCallback=1;
  
! 	// when we get here, we've set up everything! done!
  }
  
  int cm_FakeRootFid(cm_fid_t *fidp)
  {
!       fidp->cell = AFS_FAKE_ROOT_CELL_ID;            /* root cell */
!       fidp->volume = AFS_FAKE_ROOT_VOL_ID;   /* root.afs ? */
!       fidp->vnode = 0x1;
!       fidp->unique = 0x1;
!       return 0;
  }
    
  /* called directly from ioctl */
--- 105,290 ----
  /* to be called while holding freelance lock unless during init. */
  void cm_InitFakeRootDir() {
  	
!     int i, t1, t2;
!     char* currentPos;
!     int noChunks;
! 
!     // allocate space for the fake info
!     cm_dirHeader_t fakeDirHeader;
!     cm_dirEntry_t fakeEntry;
!     cm_pageHeader_t fakePageHeader;
! 
!     // i'm going to calculate how much space is needed for
!     // this fake root directory. we have these rules:
!     // 1. there are cm_noLocalMountPoints number of entries
!     // 2. each page is CM_DIR_PAGESIZE in size
!     // 3. the first 13 chunks of the first page are used for
!     //    some header stuff
!     // 4. the first chunk of all subsequent pages are used
!     //    for page header stuff
!     // 5. a max of CM_DIR_EPP entries are allowed per page
!     // 6. each entry takes 1 or more chunks, depending on 
!     //    the size of the mount point string, as determined
!     //    by cm_NameEntries
!     // 7. each chunk is CM_DIR_CHUNKSIZE bytes
! 
!     int CPP = CM_DIR_PAGESIZE / CM_DIR_CHUNKSIZE;
!     int curChunk = 13;	// chunks 0 - 12 are used for header stuff
!                         // of the first page in the directory
!     int curPage = 0;
!     int curDirEntry = 0;
!     int curDirEntryInPage = 0;
!     int sizeOfCurEntry;
!     int dirSize;
! 
!     /* Reserve 2 directory chunks for "." and ".." */
!     curChunk += 2;
! 
!     while (curDirEntry!=cm_noLocalMountPoints) {
!         sizeOfCurEntry = cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0);
!         if ((curChunk + sizeOfCurEntry >= CPP) ||
!              (curDirEntryInPage + 1 >= CM_DIR_EPP)) {
!             curPage++;
!             curDirEntryInPage = 0;
!             curChunk = 1;
!         }
!         curChunk += sizeOfCurEntry;
!         curDirEntry++;
!         curDirEntryInPage++;
!     }
  
!     dirSize = (curPage+1) *  CM_DIR_PAGESIZE;
!     cm_FakeRootDir = malloc(dirSize);
!     cm_fakeDirSize = dirSize;
! 
!     // yj: when we get here, we've figured out how much memory we need and 
!     // allocated the appropriate space for it. we now prceed to fill
!     // it up with entries.
!     curPage = 0;
!     curDirEntry = 0;
!     curDirEntryInPage = 0;
!     curChunk = 0;
! 
!     // fields in the directory entry that are unused.
!     fakeEntry.flag = 1;
!     fakeEntry.length = 0;
!     fakeEntry.next = 0;
!     fakeEntry.fid.unique = htonl(1);
! 
!     // the first page is special, it uses fakeDirHeader instead of fakePageHeader
!     // we fill up the page with dirEntries that belong there and we make changes
!     // to the fakeDirHeader.header.freeBitmap along the way. Then when we're done
!     // filling up the dirEntries in this page, we copy the fakeDirHeader into 
!     // the top of the page.
! 
!     // init the freeBitmap array
!     for (i=0; i<8; i++) 
!         fakeDirHeader.header.freeBitmap[i]=0;
! 
!     fakeDirHeader.header.freeBitmap[0] = 0xff;
!     fakeDirHeader.header.freeBitmap[1] = 0x7f;
! 
! 
!     // we start counting at 13 because the 0th to 12th chunks are used for header
!     curChunk = 13;
! 
!     // stick the first 2 entries "." and ".." in
!     fakeEntry.fid.unique = htonl(1);
!     fakeEntry.fid.vnode = htonl(1);
!     strcpy(fakeEntry.name, ".");
!     currentPos = cm_FakeRootDir + curPage * CM_DIR_PAGESIZE + curChunk * CM_DIR_CHUNKSIZE;
!     memcpy(currentPos, &fakeEntry, CM_DIR_CHUNKSIZE);
!     curChunk++; curDirEntryInPage++;
!     strcpy(fakeEntry.name, "..");
!     currentPos = cm_FakeRootDir + curPage * CM_DIR_PAGESIZE + curChunk * CM_DIR_CHUNKSIZE;
!     memcpy(currentPos, &fakeEntry, CM_DIR_CHUNKSIZE);
!     curChunk++; curDirEntryInPage++;
! 
!     // keep putting stuff into page 0 if
!     // 1. we're not done with all entries
!     // 2. we have less than CM_DIR_EPP entries in page 0
!     // 3. we're not out of chunks in page 0
! 
!     while( (curDirEntry!=cm_noLocalMountPoints) && 
!            (curDirEntryInPage < CM_DIR_EPP) &&
!            (curChunk + cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0) <= CPP)) 
!     {       
! 
!         noChunks = cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0);
!         fakeEntry.fid.vnode = htonl(curDirEntry + 2);
!         currentPos = cm_FakeRootDir + curPage * CM_DIR_PAGESIZE + curChunk * CM_DIR_CHUNKSIZE;
! 
!         memcpy(currentPos, &fakeEntry, CM_DIR_CHUNKSIZE);
!         strcpy(currentPos + 12, (cm_localMountPoints+curDirEntry)->namep);
!         curDirEntry++;
!         curDirEntryInPage++;
!         for (i=0; i<noChunks; i++) {
!             t1 = (curChunk + i) / 8;
!             t2 = curChunk + i - (t1*8);
!             fakeDirHeader.header.freeBitmap[t1] |= (1 << t2);
!         }
!         curChunk+=noChunks;
!     }
  
!     // when we get here, we're done with filling in the entries for page 0
!     // copy in the header info
  
!     memcpy(cm_FakeRootDir, &fakeDirHeader, 13 * CM_DIR_CHUNKSIZE);
  
!     curPage++;
! 
!     // ok, page 0's done. Move on to the next page.
!     while (curDirEntry!=cm_noLocalMountPoints) {
!         // setup a new page
!         curChunk = 1;			// the zeroth chunk is reserved for page header
!         curDirEntryInPage = 0; 
!         for (i=0; i<8; i++) {
!             fakePageHeader.freeBitmap[i]=0;
!         }
!         fakePageHeader.freeCount = 0;
!         fakePageHeader.pgcount = 0;
!         fakePageHeader.tag = htons(1234);
! 
!         // while we're on the same page...
!         while ( (curDirEntry!=cm_noLocalMountPoints) &&
!                 (curDirEntryInPage < CM_DIR_EPP) &&
!                 (curChunk + cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0) <= CPP))
!         {
!             // add an entry to this page
! 
!             noChunks = cm_NameEntries((cm_localMountPoints+curDirEntry)->namep, 0);
!             fakeEntry.fid.vnode=htonl(curDirEntry+2);
!             currentPos = cm_FakeRootDir + curPage * CM_DIR_PAGESIZE + curChunk * CM_DIR_CHUNKSIZE;
!             memcpy(currentPos, &fakeEntry, CM_DIR_CHUNKSIZE);
!             strcpy(currentPos + 12, (cm_localMountPoints+curDirEntry)->namep);
!             curDirEntry++;
!             curDirEntryInPage++;
!             for (i=0; i<noChunks; i++) {
!                 t1 = (curChunk + i) / 8;
!                 t2 = curChunk + i - (t1*8);
!                 fakePageHeader.freeBitmap[t1] |= (1 << t2);
!             }
!             curChunk+=noChunks;
!         }
!         memcpy(cm_FakeRootDir + curPage * CM_DIR_PAGESIZE, &fakePageHeader, sizeof(fakePageHeader));
! 
!         curPage++;
!     }
! 
!     // we know the fakeDir is setup properly, so we claim that we have callback
      osi_Log0(afsd_logp,"cm_InitFakeRootDir fakeDirCallback=1");
!     cm_fakeDirCallback=1;
  
!     // when we get here, we've set up everything! done!
  }
  
  int cm_FakeRootFid(cm_fid_t *fidp)
  {
!     fidp->cell = AFS_FAKE_ROOT_CELL_ID;            /* root cell */
!     fidp->volume = AFS_FAKE_ROOT_VOL_ID;   /* root.afs ? */
!     fidp->vnode = 0x1;
!     fidp->unique = 0x1;
!     return 0;
  }
    
  /* called directly from ioctl */
***************
*** 307,389 ****
  }
  
  int cm_reInitLocalMountPoints() {
! 	cm_fid_t aFid;
! 	int i, hash;
! 	cm_scache_t *scp, **lscpp, *tscp;
  	
! 	osi_Log0(afsd_logp,"----- freelance reinitialization starts ----- ");
  
! 	// first we invalidate all the SCPs that were created
! 	// for the local mount points
  
! 	osi_Log0(afsd_logp,"Invalidating local mount point scp...  ");
  
! 	aFid.cell = AFS_FAKE_ROOT_CELL_ID;
! 	aFid.volume=AFS_FAKE_ROOT_VOL_ID;
! 	aFid.unique=0x1;
! 	aFid.vnode=0x2;
! 
! 	lock_ObtainWrite(&cm_scacheLock);
! 	lock_ObtainMutex(&cm_Freelance_Lock);  /* always scache then freelance lock */
! 	for (i=0; i<cm_noLocalMountPoints; i++) {
! 		hash = CM_SCACHE_HASH(&aFid);
! 		for (scp=cm_hashTablep[hash]; scp; scp=scp->nextp) {
! 			if (scp->fid.volume == aFid.volume &&
! 				scp->fid.vnode == aFid.vnode &&
! 				scp->fid.unique == aFid.unique 
! 				) {
! 
! 				// mark the scp to be reused
! 				lock_ReleaseWrite(&cm_scacheLock);
! 				lock_ObtainMutex(&scp->mx);
! 				cm_DiscardSCache(scp);
! 				lock_ReleaseMutex(&scp->mx);
! 				cm_CallbackNotifyChange(scp);
! 				lock_ObtainWrite(&cm_scacheLock);
! 				scp->refCount--;
! 
! 				// take the scp out of the hash
! 				lscpp = &cm_hashTablep[hash];
! 				for (tscp=*lscpp; tscp; lscpp = &tscp->nextp, tscp = *lscpp) {
! 					if (tscp == scp) break;
! 				}
! 				*lscpp = scp->nextp;
! 				scp->flags &= ~CM_SCACHEFLAG_INHASH;
! 
! 
! 			}
! 		}
! 		aFid.vnode = aFid.vnode + 1;
! 	}
! 	lock_ReleaseWrite(&cm_scacheLock);
! 	osi_Log0(afsd_logp,"\tall old scp cleared!");
! 
! 	// we must free the memory that was allocated in the prev
! 	// cm_InitLocalMountPoints call
! 	osi_Log0(afsd_logp,"Removing old localmountpoints...  ");
! 	free(cm_localMountPoints);
! 	osi_Log0(afsd_logp,"\tall old localmountpoints cleared!");
! 
! 	// now re-init the localmountpoints
! 	osi_Log0(afsd_logp,"Creating new localmountpoints...  ");
! 	cm_InitLocalMountPoints();
! 	osi_Log0(afsd_logp,"\tcreated new set of localmountpoints!");
! 	
! 	
! 	// now we have to free the memory allocated in cm_initfakerootdir
! 	osi_Log0(afsd_logp,"Removing old fakedir...  ");
! 	free(cm_FakeRootDir);
! 	osi_Log0(afsd_logp,"\t\told fakedir removed!");
! 
! 	// then we re-create that dir
! 	osi_Log0(afsd_logp,"Creating new fakedir...  ");
! 	cm_InitFakeRootDir();
! 	osi_Log0(afsd_logp,"\t\tcreated new fakedir!");
  
! 	lock_ReleaseMutex(&cm_Freelance_Lock);
  
! 	osi_Log0(afsd_logp,"----- freelance reinit complete -----");
! 	return 0;
  }
  
  
--- 307,386 ----
  }
  
  int cm_reInitLocalMountPoints() {
!     cm_fid_t aFid;
!     int i, hash;
!     cm_scache_t *scp, **lscpp, *tscp;
  	
!     osi_Log0(afsd_logp,"----- freelance reinitialization starts ----- ");
  
!     // first we invalidate all the SCPs that were created
!     // for the local mount points
  
!     osi_Log0(afsd_logp,"Invalidating local mount point scp...  ");
  
!     aFid.cell = AFS_FAKE_ROOT_CELL_ID;
!     aFid.volume=AFS_FAKE_ROOT_VOL_ID;
!     aFid.unique=0x1;
!     aFid.vnode=0x2;
! 
!     lock_ObtainWrite(&cm_scacheLock);
!     lock_ObtainMutex(&cm_Freelance_Lock);  /* always scache then freelance lock */
!     for (i=0; i<cm_noLocalMountPoints; i++) {
!         hash = CM_SCACHE_HASH(&aFid);
!         for (scp=cm_hashTablep[hash]; scp; scp=scp->nextp) {
!             if (scp->fid.volume == aFid.volume &&
!                  scp->fid.vnode == aFid.vnode &&
!                  scp->fid.unique == aFid.unique 
!                  ) {
! 
!                 // mark the scp to be reused
!                 lock_ReleaseWrite(&cm_scacheLock);
!                 lock_ObtainMutex(&scp->mx);
!                 cm_DiscardSCache(scp);
!                 lock_ReleaseMutex(&scp->mx);
!                 cm_CallbackNotifyChange(scp);
!                 lock_ObtainWrite(&cm_scacheLock);
!                 scp->refCount--;
! 
!                 // take the scp out of the hash
!                 lscpp = &cm_hashTablep[hash];
!                 for (tscp=*lscpp; tscp; lscpp = &tscp->nextp, tscp = *lscpp) {
!                     if (tscp == scp) break;
!                 }
!                 *lscpp = scp->nextp;
!                 scp->flags &= ~CM_SCACHEFLAG_INHASH;
!             }
!         }
!         aFid.vnode = aFid.vnode + 1;
!     }
!     lock_ReleaseWrite(&cm_scacheLock);
!     osi_Log0(afsd_logp,"\tall old scp cleared!");
  
!     // we must free the memory that was allocated in the prev
!     // cm_InitLocalMountPoints call
!     osi_Log0(afsd_logp,"Removing old localmountpoints...  ");
!     free(cm_localMountPoints);
!     osi_Log0(afsd_logp,"\tall old localmountpoints cleared!");
! 
!     // now re-init the localmountpoints
!     osi_Log0(afsd_logp,"Creating new localmountpoints...  ");
!     cm_InitLocalMountPoints();
!     osi_Log0(afsd_logp,"\tcreated new set of localmountpoints!");
! 
!     // now we have to free the memory allocated in cm_initfakerootdir
!     osi_Log0(afsd_logp,"Removing old fakedir...  ");
!     free(cm_FakeRootDir);
!     osi_Log0(afsd_logp,"\t\told fakedir removed!");
! 
!     // then we re-create that dir
!     osi_Log0(afsd_logp,"Creating new fakedir...  ");
!     cm_InitFakeRootDir();
!     osi_Log0(afsd_logp,"\t\tcreated new fakedir!");
  
!     lock_ReleaseMutex(&cm_Freelance_Lock);
! 
!     osi_Log0(afsd_logp,"----- freelance reinit complete -----");
!     return 0;
  }
  
  
***************
*** 392,403 ****
  // process for the freelance client.
  /* to be called while holding freelance lock unless during init. */
  long cm_InitLocalMountPoints() {
! 	FILE *fp;
      int i;
! 	char line[512];
! 	char* t;
! 	cm_localMountPoint_t* aLocalMountPoint;
! 	char hdir[120];
      long code;
      char rootCellName[256];
  #if !defined(DJGPP)
--- 389,400 ----
  // process for the freelance client.
  /* to be called while holding freelance lock unless during init. */
  long cm_InitLocalMountPoints() {
!     FILE *fp;
      int i;
!     char line[512];
!     char*t, *t2;
!     cm_localMountPoint_t* aLocalMountPoint;
!     char hdir[120];
      long code;
      char rootCellName[256];
  #if !defined(DJGPP)
***************
*** 465,470 ****
--- 462,472 ----
              RegEnumValue( hkFreelance, dwIndex, szValueName, &dwValueSize, NULL,
                            &dwType, line, &dwSize);
  
+             /* find the trailing dot; null terminate after it */
+             t2 = strrchr(line, '.');
+             if (t2)
+                 *(t2+1) = '\0';
+ 
              // line is not empty, so let's parse it
              t = strchr(line, '#');
              if (!t)
***************
*** 477,488 ****
                  continue;
              }
              aLocalMountPoint->namep=malloc(t-line+1);
!             memcpy(aLocalMountPoint->namep, line, t-line);
!             *(aLocalMountPoint->namep + (t-line)) = 0;
  		
!             aLocalMountPoint->mountPointStringp=malloc(strlen(line) - (t-line) + 1);
!             memcpy(aLocalMountPoint->mountPointStringp, t, strlen(line)-(t-line)-2);
!             *(aLocalMountPoint->mountPointStringp + (strlen(line)-(t-line)-2)) = 0;
      
              osi_Log2(afsd_logp,"found mount point: name %s, string %s",
                        osi_LogSaveString(afsd_logp,aLocalMountPoint->namep),
--- 479,491 ----
                  continue;
              }
              aLocalMountPoint->namep=malloc(t-line+1);
!             strncpy(aLocalMountPoint->namep, line, t-line);
!             aLocalMountPoint->namep[t-line] = '\0';
  		
!             /* copy the mount point string without the trailing dot */
!             aLocalMountPoint->mountPointStringp=malloc(strlen(t));
! 			strncpy(aLocalMountPoint->mountPointStringp, t, strlen(t)-1);
! 			aLocalMountPoint->mountPointStringp[strlen(t)-1] = '\0';
      
              osi_Log2(afsd_logp,"found mount point: name %s, string %s",
                        osi_LogSaveString(afsd_logp,aLocalMountPoint->namep),
***************
*** 499,511 ****
      /* What follows is the old code to read freelance mount points 
       * out of a text file modified to copy the data into the registry
       */
! 	cm_GetConfigDir(hdir);
! 	strcat(hdir, AFS_FREELANCE_INI);
! 	// open the ini file for reading
! 	fp = fopen(hdir, "r");
  
! 	// if we fail to open the file, create an empty one
! 	if (!fp) {
          fp = fopen(hdir, "w");
        	code = cm_GetRootCellName(rootCellName);
          if (code == 0) {
--- 502,514 ----
      /* What follows is the old code to read freelance mount points 
       * out of a text file modified to copy the data into the registry
       */
!     cm_GetConfigDir(hdir);
!     strcat(hdir, AFS_FREELANCE_INI);
!     // open the ini file for reading
!     fp = fopen(hdir, "r");
  
!     // if we fail to open the file, create an empty one
!     if (!fp) {
          fp = fopen(hdir, "w");
        	code = cm_GetRootCellName(rootCellName);
          if (code == 0) {
***************
*** 519,528 ****
              fclose(fp);
              return 0;  /* success */
          }
! 	}
  
! 	// we successfully opened the file
! 	osi_Log0(afsd_logp,"opened afs_freelance.ini");
  	
  #if !defined(DJGPP)
      RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
--- 522,531 ----
              fclose(fp);
              return 0;  /* success */
          }
!     }
  
!     // we successfully opened the file
!     osi_Log0(afsd_logp,"opened afs_freelance.ini");
  	
  #if !defined(DJGPP)
      RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
***************
*** 537,577 ****
      dwIndex = 0;
  #endif
  
! 	// now we read the first line to see how many entries
! 	// there are
! 	fgets(line, sizeof(line), fp);
! 
! 	// if the line is empty at any point when we're reading
! 	// we're screwed. report error and return.
! 	if (*line==0) {
! 		afsi_log("error occurred while reading afs_freelance.ini");
! 		fprintf(stderr, "error occurred while reading afs_freelance.ini");
! 		return -1;
! 	}
! 
! 	// get the number of entries there are from the first line
! 	// that we read
! 	cm_noLocalMountPoints = atoi(line);
! 
! 	// create space to store the local mount points
! 	cm_localMountPoints = malloc(sizeof(cm_localMountPoint_t) * cm_noLocalMountPoints);
! 	aLocalMountPoint = cm_localMountPoints;
! 		
! 	// now we read n lines and parse them into local mount points
! 	// where n is the number of local mount points there are, as
! 	// determined above.
! 	// Each line in the ini file represents 1 local mount point and 
! 	// is in the format xxx#yyy:zzz, where xxx is the directory
! 	// entry name, yyy is the cell name and zzz is the volume name.
! 	// #yyy:zzz together make up the mount point.
! 	for (i=0; i<cm_noLocalMountPoints; i++) {
! 		fgets(line, sizeof(line), fp);
! 		// check that the line is not empty
! 		if (line[0]==0) {
! 			afsi_log("error occurred while parsing entry in %s: empty line in line %d", AFS_FREELANCE_INI, i);
! 			fprintf(stderr, "error occurred while parsing entry in afs_freelance.ini: empty line in line %d", i);
! 			return -1;
! 		}
  
  #if !defined(DJGPP)
          if ( hkFreelance ) {
--- 540,580 ----
      dwIndex = 0;
  #endif
  
!     // now we read the first line to see how many entries
!     // there are
!     fgets(line, sizeof(line), fp);
! 
!     // if the line is empty at any point when we're reading
!     // we're screwed. report error and return.
!     if (*line==0) {
!         afsi_log("error occurred while reading afs_freelance.ini");
!         fprintf(stderr, "error occurred while reading afs_freelance.ini");
!         return -1;
!     }
! 
!     // get the number of entries there are from the first line
!     // that we read
!     cm_noLocalMountPoints = atoi(line);
! 
!     // create space to store the local mount points
!     cm_localMountPoints = malloc(sizeof(cm_localMountPoint_t) * cm_noLocalMountPoints);
!     aLocalMountPoint = cm_localMountPoints;
! 
!     // now we read n lines and parse them into local mount points
!     // where n is the number of local mount points there are, as
!     // determined above.
!     // Each line in the ini file represents 1 local mount point and 
!     // is in the format xxx#yyy:zzz, where xxx is the directory
!     // entry name, yyy is the cell name and zzz is the volume name.
!     // #yyy:zzz together make up the mount point.
!     for (i=0; i<cm_noLocalMountPoints; i++) {
!         fgets(line, sizeof(line), fp);
!         // check that the line is not empty
!         if (line[0]==0) {
!             afsi_log("error occurred while parsing entry in %s: empty line in line %d", AFS_FREELANCE_INI, i);
!             fprintf(stderr, "error occurred while parsing entry in afs_freelance.ini: empty line in line %d", i);
!             return -1;
!         }
  
  #if !defined(DJGPP)
          if ( hkFreelance ) {
***************
*** 584,629 ****
          }
  #endif 
  
! 		// line is not empty, so let's parse it
! 		t = strchr(line, '#');
          if (!t)
              t = strchr(line, '%');
! 		// make sure that there is a '#' or '%' separator in the line
! 		if (!t) {
! 			afsi_log("error occurred while parsing entry in %s: no # or %% separator in line %d", AFS_FREELANCE_INI, i);
! 			fprintf(stderr, "error occurred while parsing entry in afs_freelance.ini: no # or %% separator in line %d", i);
! 			return -1;
! 		}
! 		aLocalMountPoint->namep=malloc(t-line+1);
! 		memcpy(aLocalMountPoint->namep, line, t-line);
! 		*(aLocalMountPoint->namep + (t-line)) = 0;
! 		
          aLocalMountPoint->mountPointStringp=malloc(strlen(line) - (t-line) + 1);
! 		memcpy(aLocalMountPoint->mountPointStringp, t, strlen(line)-(t-line)-2);
! 		*(aLocalMountPoint->mountPointStringp + (strlen(line)-(t-line)-2)) = 0;
!     
          osi_Log2(afsd_logp,"found mount point: name %s, string %s",
                    aLocalMountPoint->namep,
                    aLocalMountPoint->mountPointStringp);
  
          aLocalMountPoint++;
! 	}
! 	fclose(fp);
  #if !defined(DJGPP)
      if ( hkFreelance ) {
          RegCloseKey(hkFreelance);
          DeleteFile(hdir);
      }
  #endif
! 	return 0;
  }
  
  int cm_getNoLocalMountPoints() {
! 	return cm_noLocalMountPoints;
  }
  
  cm_localMountPoint_t* cm_getLocalMountPoint(int vnode) {
! 	return 0;
  }
  
  long cm_FreelanceAddMount(char *filename, char *cellname, char *volume, int rw, cm_fid_t *fidp)
--- 587,632 ----
          }
  #endif 
  
!         // line is not empty, so let's parse it
!         t = strchr(line, '#');
          if (!t)
              t = strchr(line, '%');
!         // make sure that there is a '#' or '%' separator in the line
!         if (!t) {
!             afsi_log("error occurred while parsing entry in %s: no # or %% separator in line %d", AFS_FREELANCE_INI, i);
!             fprintf(stderr, "error occurred while parsing entry in afs_freelance.ini: no # or %% separator in line %d", i);
!             return -1;
!         }
!         aLocalMountPoint->namep=malloc(t-line+1);
!         memcpy(aLocalMountPoint->namep, line, t-line);
!         *(aLocalMountPoint->namep + (t-line)) = 0;
! 
          aLocalMountPoint->mountPointStringp=malloc(strlen(line) - (t-line) + 1);
!         memcpy(aLocalMountPoint->mountPointStringp, t, strlen(line)-(t-line)-2);
!         *(aLocalMountPoint->mountPointStringp + (strlen(line)-(t-line)-2)) = 0;
! 
          osi_Log2(afsd_logp,"found mount point: name %s, string %s",
                    aLocalMountPoint->namep,
                    aLocalMountPoint->mountPointStringp);
  
          aLocalMountPoint++;
!     }
!     fclose(fp);
  #if !defined(DJGPP)
      if ( hkFreelance ) {
          RegCloseKey(hkFreelance);
          DeleteFile(hdir);
      }
  #endif
!     return 0;
  }
  
  int cm_getNoLocalMountPoints() {
!     return cm_noLocalMountPoints;
  }
  
  cm_localMountPoint_t* cm_getLocalMountPoint(int vnode) {
!     return 0;
  }
  
  long cm_FreelanceAddMount(char *filename, char *cellname, char *volume, int rw, cm_fid_t *fidp)
Index: openafs/src/WINNT/afsd/cm_freelance.h
diff -c openafs/src/WINNT/afsd/cm_freelance.h:1.6 openafs/src/WINNT/afsd/cm_freelance.h:1.6.2.1
*** openafs/src/WINNT/afsd/cm_freelance.h:1.6	Sat Aug  7 01:44:05 2004
--- openafs/src/WINNT/afsd/cm_freelance.h	Mon Oct 18 00:09:26 2004
***************
*** 17,27 ****
  extern long cm_FreelanceRemoveMount(char *toremove);
  extern long cm_FreelanceAddMount(char *filename, char *cellname, char *volume, int rw, cm_fid_t *fidp);
  extern int cm_clearLocalMountPointChange();
! 
  
  #define AFS_FREELANCE_INI "afs_freelance.ini"
  #define AFS_FAKE_ROOT_CELL_ID 0xFFFFFFFF
  #define AFS_FAKE_ROOT_VOL_ID  0xFFFFFFFF
  
! extern afs_uint32 FakeFreelanceModTime;
  #endif // _CM_FREELANCE_H
--- 17,27 ----
  extern long cm_FreelanceRemoveMount(char *toremove);
  extern long cm_FreelanceAddMount(char *filename, char *cellname, char *volume, int rw, cm_fid_t *fidp);
  extern int cm_clearLocalMountPointChange();
! extern int cm_FakeRootFid(cm_fid_t *fidp);
  
  #define AFS_FREELANCE_INI "afs_freelance.ini"
  #define AFS_FAKE_ROOT_CELL_ID 0xFFFFFFFF
  #define AFS_FAKE_ROOT_VOL_ID  0xFFFFFFFF
  
! extern time_t FakeFreelanceModTime;
  #endif // _CM_FREELANCE_H
Index: openafs/src/WINNT/afsd/cm_ioctl.c
diff -c openafs/src/WINNT/afsd/cm_ioctl.c:1.33.2.1 openafs/src/WINNT/afsd/cm_ioctl.c:1.33.2.4
*** openafs/src/WINNT/afsd/cm_ioctl.c:1.33.2.1	Tue Aug 17 00:28:39 2004
--- openafs/src/WINNT/afsd/cm_ioctl.c	Mon Oct 18 00:09:26 2004
***************
*** 39,44 ****
--- 39,45 ----
  #endif
  
  #include "cm_rpc.h"
+ #include <strsafe.h>
  
  #ifdef _DEBUG
  #include <crtdbg.h>
***************
*** 53,79 ****
  extern afs_int32 cryptall;
  extern char cm_NetbiosName[];
  
  void cm_InitIoctl(void)
  {
! 	lock_InitializeMutex(&cm_Afsdsbmt_Lock, "AFSDSBMT.INI Access Lock");
  }
  
  long cm_FlushFile(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
  {
! 	long code;
  
! 	lock_ObtainWrite(&scp->bufCreateLock);
! 	code = buf_FlushCleanPages(scp, userp, reqp);
          
!         lock_ObtainMutex(&scp->mx);
! 	scp->cbServerp = NULL;
!         scp->cbExpires = 0;
!         lock_ReleaseMutex(&scp->mx);
  
! 	lock_ReleaseWrite(&scp->bufCreateLock);
! 	cm_dnlcPurgedp(scp);
  
!         return code;
  }
  
  /*
--- 54,82 ----
  extern afs_int32 cryptall;
  extern char cm_NetbiosName[];
  
+ extern void afsi_log(char *pattern, ...);
+ 
  void cm_InitIoctl(void)
  {
!     lock_InitializeMutex(&cm_Afsdsbmt_Lock, "AFSDSBMT.INI Access Lock");
  }
  
  long cm_FlushFile(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
  {
!     long code;
  
!     lock_ObtainWrite(&scp->bufCreateLock);
!     code = buf_FlushCleanPages(scp, userp, reqp);
          
!     lock_ObtainMutex(&scp->mx);
!     scp->cbServerp = NULL;
!     scp->cbExpires = 0;
!     lock_ReleaseMutex(&scp->mx);
  
!     lock_ReleaseWrite(&scp->bufCreateLock);
!     cm_dnlcPurgedp(scp);
  
!     return code;
  }
  
  /*
***************
*** 82,104 ****
   */
  void cm_ResetACLCache(cm_user_t *userp)
  {
! 	cm_scache_t *scp;
! 	int hash;
  
! 	lock_ObtainWrite(&cm_scacheLock);
! 	for (hash=0; hash < cm_hashTableSize; hash++) {
! 		for (scp=cm_hashTablep[hash]; scp; scp=scp->nextp) {
! 			scp->refCount++;
! 			lock_ReleaseWrite(&cm_scacheLock);
! 			lock_ObtainMutex(&scp->mx);
! 			cm_InvalidateACLUser(scp, userp);
! 			lock_ReleaseMutex(&scp->mx);
! 			lock_ObtainWrite(&cm_scacheLock);
! 			scp->refCount--;
! 		}
! 	}
! 	lock_ReleaseWrite(&cm_scacheLock);
! }
  
  /*
   *  TranslateExtendedChars - This is a fix for TR 54482.
--- 85,107 ----
   */
  void cm_ResetACLCache(cm_user_t *userp)
  {
!     cm_scache_t *scp;
!     int hash;
  
!     lock_ObtainWrite(&cm_scacheLock);
!     for (hash=0; hash < cm_hashTableSize; hash++) {
!         for (scp=cm_hashTablep[hash]; scp; scp=scp->nextp) {
!             scp->refCount++;
!             lock_ReleaseWrite(&cm_scacheLock);
!             lock_ObtainMutex(&scp->mx);
!             cm_InvalidateACLUser(scp, userp);
!             lock_ReleaseMutex(&scp->mx);
!             lock_ObtainWrite(&cm_scacheLock);
!             scp->refCount--;
!         }
!     }
!     lock_ReleaseWrite(&cm_scacheLock);
! }       
  
  /*
   *  TranslateExtendedChars - This is a fix for TR 54482.
***************
*** 136,143 ****
  long cm_ParseIoctlPath(smb_ioctl_t *ioctlp, cm_user_t *userp, cm_req_t *reqp,
  	cm_scache_t **scpp)
  {
! 	long code;
! 	cm_scache_t *substRootp;
      char * relativePath = ioctlp->inDatap;
  
      /* This is usually the file name, but for StatMountPoint it is the path. */
--- 139,146 ----
  long cm_ParseIoctlPath(smb_ioctl_t *ioctlp, cm_user_t *userp, cm_req_t *reqp,
  	cm_scache_t **scpp)
  {
!     long code;
!     cm_scache_t *substRootp;
      char * relativePath = ioctlp->inDatap;
  
      /* This is usually the file name, but for StatMountPoint it is the path. */
***************
*** 147,153 ****
       *    \\netbios-name\submount\path\.
       *    \\netbios-name\submount\path\file
       */
! 	TranslateExtendedChars(relativePath);
  
      if (relativePath[0] == relativePath[1] &&
           relativePath[1] == '\\' && 
--- 150,156 ----
       *    \\netbios-name\submount\path\.
       *    \\netbios-name\submount\path\file
       */
!     TranslateExtendedChars(relativePath);
  
      if (relativePath[0] == relativePath[1] &&
           relativePath[1] == '\\' && 
***************
*** 180,246 ****
                               CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
                               userp, sharePath, reqp, &substRootp);
              free(sharePath);
!             if (code) return code;
  
! 			code = cm_NameI(substRootp, p, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
!                          userp, NULL, reqp, scpp);
! 			if (code) return code;
          } else {
              /* otherwise, treat the name as a cellname mounted off the afs root.
! 			 * This requires that we reconstruct the shareName string with 
! 			 * leading and trailing slashes.
! 			 */
              p = relativePath + 2 + strlen(cm_NetbiosName) + 1;
! 			if ( !_strnicmp("all", p, 3) )
! 				p += 4;
  
- 			shareName[0] = '/';
- 			for (i = 1; *p && *p != '\\'; i++,p++ ) {
- 				shareName[i] = *p;
- 			}
- 			p++;                    /* skip past trailing slash */
- 			shareName[i++] = '/';	/* add trailing slash */
- 			shareName[i] = 0;       /* terminate string */
  
! 			
! 			code = cm_NameI(cm_rootSCachep, ioctlp->prefix->data,
                               CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
                               userp, shareName, reqp, &substRootp);
!             if (code) return code;
  
! 			code = cm_NameI(substRootp, p, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
!                          userp, NULL, reqp, scpp);
! 			if (code) return code;
          }
      } else {
          code = cm_NameI(cm_rootSCachep, ioctlp->prefix->data,
                           CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
                           userp, ioctlp->tidPathp, reqp, &substRootp);
!         if (code) return code;
          
          code = cm_NameI(substRootp, relativePath, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
                           userp, NULL, reqp, scpp);
!         if (code) return code;
      }
  
! 	/* # of bytes of path */
      code = strlen(ioctlp->inDatap) + 1;
      ioctlp->inDatap += code;
  
      /* This is usually nothing, but for StatMountPoint it is the file name. */
      TranslateExtendedChars(ioctlp->inDatap);
  
! 	/* and return success */
      return 0;
  }
  
  void cm_SkipIoctlPath(smb_ioctl_t *ioctlp)
  {
! 	long temp;
          
!         temp = strlen(ioctlp->inDatap) + 1;
!         ioctlp->inDatap += temp;
! }
  
  
  /* format the specified path to look like "/afs/<cellname>/usr", by
--- 183,255 ----
                               CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
                               userp, sharePath, reqp, &substRootp);
              free(sharePath);
!             if (code) 
!                 return code;
  
!             code = cm_NameI(substRootp, p, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
!                              userp, NULL, reqp, scpp);
!             if (code) 
!                 return code;
          } else {
              /* otherwise, treat the name as a cellname mounted off the afs root.
!              * This requires that we reconstruct the shareName string with 
!              * leading and trailing slashes.
!              */
              p = relativePath + 2 + strlen(cm_NetbiosName) + 1;
!             if ( !_strnicmp("all", p, 3) )
!                 p += 4;
! 
!             shareName[0] = '/';
!             for (i = 1; *p && *p != '\\'; i++,p++ ) {
!                 shareName[i] = *p;
!             }
!             p++;                    /* skip past trailing slash */
!             shareName[i++] = '/';	/* add trailing slash */
!             shareName[i] = 0;       /* terminate string */
  
  
!             code = cm_NameI(cm_rootSCachep, ioctlp->prefix->data,
                               CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
                               userp, shareName, reqp, &substRootp);
!             if (code) 
!                 return code;
  
!             code = cm_NameI(substRootp, p, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
!                             userp, NULL, reqp, scpp);
!             if (code) 
!                 return code;
          }
      } else {
          code = cm_NameI(cm_rootSCachep, ioctlp->prefix->data,
                           CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
                           userp, ioctlp->tidPathp, reqp, &substRootp);
!         if (code) 
!             return code;
          
          code = cm_NameI(substRootp, relativePath, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
                           userp, NULL, reqp, scpp);
!         if (code) 
!             return code;
      }
  
!     /* # of bytes of path */
      code = strlen(ioctlp->inDatap) + 1;
      ioctlp->inDatap += code;
  
      /* This is usually nothing, but for StatMountPoint it is the file name. */
      TranslateExtendedChars(ioctlp->inDatap);
  
!     /* and return success */
      return 0;
  }
  
  void cm_SkipIoctlPath(smb_ioctl_t *ioctlp)
  {
!     long temp;
          
!     temp = strlen(ioctlp->inDatap) + 1;
!     ioctlp->inDatap += temp;
! }       
  
  
  /* format the specified path to look like "/afs/<cellname>/usr", by
***************
*** 252,284 ****
   */
  void cm_NormalizeAfsPath (char *outpathp, char *inpathp)
  {
! 	char *cp;
      char bslash_mountRoot[256];
         
      strncpy(bslash_mountRoot, cm_mountRoot, sizeof(bslash_mountRoot) - 1);
      bslash_mountRoot[0] = '\\';
         
      if (!strnicmp (inpathp, cm_mountRoot, strlen(cm_mountRoot)))
! 		lstrcpy (outpathp, inpathp);
!        else if (!strnicmp (inpathp, bslash_mountRoot, strlen(bslash_mountRoot)))
! 		lstrcpy (outpathp, inpathp);
! 	else if ((inpathp[0] == '/') || (inpathp[0] == '\\'))
!                sprintf (outpathp, "%s%s", cm_mountRoot, inpathp);
! 	else // inpathp looks like "<cell>/usr"
!                sprintf (outpathp, "%s/%s", cm_mountRoot, inpathp);
! 
! 	for (cp = outpathp; *cp != 0; ++cp) {
! 		if (*cp == '\\')
! 			*cp = '/';
! 	}
! 
! 	if (strlen(outpathp) && (outpathp[strlen(outpathp)-1] == '/')) {
!            outpathp[strlen(outpathp)-1] = 0;
! 	}
  
! 	if (!strcmpi (outpathp, cm_mountRoot)) {
          strcpy (outpathp, cm_mountRoot);
! 	}
  }
  
  /* parse the passed-in file name and do a namei on its parent.  If we fail,
--- 261,293 ----
   */
  void cm_NormalizeAfsPath (char *outpathp, char *inpathp)
  {
!     char *cp;
      char bslash_mountRoot[256];
         
      strncpy(bslash_mountRoot, cm_mountRoot, sizeof(bslash_mountRoot) - 1);
      bslash_mountRoot[0] = '\\';
         
      if (!strnicmp (inpathp, cm_mountRoot, strlen(cm_mountRoot)))
!         lstrcpy (outpathp, inpathp);
!     else if (!strnicmp (inpathp, bslash_mountRoot, strlen(bslash_mountRoot)))
!         lstrcpy (outpathp, inpathp);
!     else if ((inpathp[0] == '/') || (inpathp[0] == '\\'))
!         sprintf (outpathp, "%s%s", cm_mountRoot, inpathp);
!     else // inpathp looks like "<cell>/usr"
!         sprintf (outpathp, "%s/%s", cm_mountRoot, inpathp);
! 
!     for (cp = outpathp; *cp != 0; ++cp) {
!         if (*cp == '\\')
!             *cp = '/';
!     }       
  
!     if (strlen(outpathp) && (outpathp[strlen(outpathp)-1] == '/')) {
!         outpathp[strlen(outpathp)-1] = 0;
!     }
! 
!     if (!strcmpi (outpathp, cm_mountRoot)) {
          strcpy (outpathp, cm_mountRoot);
!     }
  }
  
  /* parse the passed-in file name and do a namei on its parent.  If we fail,
***************
*** 287,314 ****
  long cm_ParseIoctlParent(smb_ioctl_t *ioctlp, cm_user_t *userp, cm_req_t *reqp,
  			 cm_scache_t **scpp, char *leafp)
  {
! 	long code;
      char tbuffer[1024];
      char *tp, *jp;
! 	cm_scache_t *substRootp;
  
! 	strcpy(tbuffer, ioctlp->inDatap);
      tp = strrchr(tbuffer, '\\');
! 	jp = strrchr(tbuffer, '/');
! 	if (!tp)
! 		tp = jp;
! 	else if (jp && (tp - tbuffer) < (jp - tbuffer))
! 		tp = jp;
      if (!tp) {
          strcpy(tbuffer, "\\");
          if (leafp) 
              strcpy(leafp, ioctlp->inDatap);
! 	}
      else {
          *tp = 0;
          if (leafp) 
              strcpy(leafp, tp+1);
! 	}   
  
      if (tbuffer[0] == tbuffer[1] &&
          tbuffer[1] == '\\' && 
--- 296,323 ----
  long cm_ParseIoctlParent(smb_ioctl_t *ioctlp, cm_user_t *userp, cm_req_t *reqp,
  			 cm_scache_t **scpp, char *leafp)
  {
!     long code;
      char tbuffer[1024];
      char *tp, *jp;
!     cm_scache_t *substRootp;
  
!     strcpy(tbuffer, ioctlp->inDatap);
      tp = strrchr(tbuffer, '\\');
!     jp = strrchr(tbuffer, '/');
!     if (!tp)
!         tp = jp;
!     else if (jp && (tp - tbuffer) < (jp - tbuffer))
!         tp = jp;
      if (!tp) {
          strcpy(tbuffer, "\\");
          if (leafp) 
              strcpy(leafp, ioctlp->inDatap);
!     }
      else {
          *tp = 0;
          if (leafp) 
              strcpy(leafp, tp+1);
!     }   
  
      if (tbuffer[0] == tbuffer[1] &&
          tbuffer[1] == '\\' && 
***************
*** 343,1064 ****
              free(sharePath);
              if (code) return code;
  
! 			code = cm_NameI(substRootp, p, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
!                          userp, NULL, reqp, scpp);
! 			if (code) return code;
          } else {
              /* otherwise, treat the name as a cellname mounted off the afs root.
! 			 * This requires that we reconstruct the shareName string with 
! 			 * leading and trailing slashes.
! 			 */
              p = tbuffer + 2 + strlen(cm_NetbiosName) + 1;
! 			if ( !_strnicmp("all", p, 3) )
! 				p += 4;
  
! 			shareName[0] = '/';
! 			for (i = 1; *p && *p != '\\'; i++,p++ ) {
! 				shareName[i] = *p;
! 			}
! 			p++;                    /* skip past trailing slash */
! 			shareName[i++] = '/';	/* add trailing slash */
! 			shareName[i] = 0;       /* terminate string */
! 			
! 			code = cm_NameI(cm_rootSCachep, ioctlp->prefix->data,
                               CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
                               userp, shareName, reqp, &substRootp);
              if (code) return code;
  
! 			code = cm_NameI(substRootp, p, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
!                          userp, NULL, reqp, scpp);
! 			if (code) return code;
          }
      } else {
          code = cm_NameI(cm_rootSCachep, ioctlp->prefix->data,
!                     CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
!                     userp, ioctlp->tidPathp, reqp, &substRootp);
          if (code) return code;
  
          code = cm_NameI(substRootp, tbuffer, CM_FLAG_FOLLOW,
!                     userp, NULL, reqp, scpp);
          if (code) return code;
      }
  
! 	/* # of bytes of path */
!         code = strlen(ioctlp->inDatap) + 1;
!         ioctlp->inDatap += code;
  
! 	/* and return success */
!         return 0;
  }
  
  long cm_IoctlGetACL(smb_ioctl_t *ioctlp, cm_user_t *userp)
  {
! 	cm_conn_t *connp;
!         cm_scache_t *scp;
!         AFSOpaque acl;
!         AFSFetchStatus fileStatus;
!         AFSVolSync volSync;
!         long code;
!         AFSFid fid;
!         int tlen;
! 	cm_req_t req;
  
!         cm_InitReq(&req);
  
!         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
!         if (code) return code;
! 	
! 	/* now make the get acl call */
! 	fid.Volume = scp->fid.volume;
!         fid.Vnode = scp->fid.vnode;
!         fid.Unique = scp->fid.unique;
! 	do {
! 	        acl.AFSOpaque_val = ioctlp->outDatap;
! 	        acl.AFSOpaque_len = 0;
! 		code = cm_Conn(&scp->fid, userp, &req, &connp);
!                 if (code) continue;
!                 
!                 code = RXAFS_FetchACL(connp->callp, &fid, &acl, &fileStatus, &volSync);
! 	} while (cm_Analyze(connp, userp, &req, &scp->fid, &volSync, NULL, NULL, code));
! 	code = cm_MapRPCError(code, &req);
! 	cm_ReleaseSCache(scp);
!         
!         if (code) return code;
!         
! 	/* skip over return data */
!         tlen = strlen(ioctlp->outDatap) + 1;
!         ioctlp->outDatap += tlen;
  
! 	/* and return success */
!         return 0;
  }
  
  long cm_IoctlGetFileCellName(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	long code;
!         cm_scache_t *scp;
!         cm_cell_t *cellp;
! 	cm_req_t req;
  
! 	cm_InitReq(&req);
  
!         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
!         if (code) return code;
!         
!         cellp = cm_FindCellByID(scp->fid.cell);
!         if (cellp) {
! 		strcpy(ioctlp->outDatap, cellp->namep);
!                 ioctlp->outDatap += strlen(ioctlp->outDatap) + 1;
!                 code = 0;
!         }
!         else code = CM_ERROR_NOSUCHCELL;
!         
!         cm_ReleaseSCache(scp);
!         return code;
  }
  
  long cm_IoctlSetACL(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	cm_conn_t *connp;
!         cm_scache_t *scp;
!         AFSOpaque acl;
!         AFSFetchStatus fileStatus;
!         AFSVolSync volSync;
!         long code;
!         AFSFid fid;
! 	cm_req_t req;
  
!         cm_InitReq(&req);
  
!         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
!         if (code) return code;
  	
! 	/* now make the get acl call */
! 	fid.Volume = scp->fid.volume;
!         fid.Vnode = scp->fid.vnode;
!         fid.Unique = scp->fid.unique;
! 	do {
! 	        acl.AFSOpaque_val = ioctlp->inDatap;
! 	        acl.AFSOpaque_len = strlen(ioctlp->inDatap)+1;
! 		code = cm_Conn(&scp->fid, userp, &req, &connp);
!                 if (code) continue;
!                 
!                 code = RXAFS_StoreACL(connp->callp, &fid, &acl, &fileStatus, &volSync);
! 	} while (cm_Analyze(connp, userp, &req, &scp->fid, &volSync, NULL, NULL, code));
! 	code = cm_MapRPCError(code, &req);
! 
! 	/* invalidate cache info, since we just trashed the ACL cache */
! 	lock_ObtainMutex(&scp->mx);
!         cm_DiscardSCache(scp);
! 	lock_ReleaseMutex(&scp->mx);
  
! 	cm_ReleaseSCache(scp);
!         
!         return code;
  }
  
  long cm_IoctlFlushVolume(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	long code;
!         cm_scache_t *scp;
!         unsigned long volume;
!         int i;
! 	cm_req_t req;
  
!         cm_InitReq(&req);
  
!         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
!         if (code) return code;
          
! 	volume = scp->fid.volume;
!         cm_ReleaseSCache(scp);
  
! 	lock_ObtainWrite(&cm_scacheLock);
! 	for(i=0; i<cm_hashTableSize; i++) {
! 		for(scp = cm_hashTablep[i]; scp; scp = scp->nextp) {
! 			if (scp->fid.volume == volume) {
! 				scp->refCount++;
!                                 lock_ReleaseWrite(&cm_scacheLock);
! 
! 				/* now flush the file */
! 				cm_FlushFile(scp, userp, &req);
! 
!                                 lock_ObtainWrite(&cm_scacheLock);
!                                 scp->refCount--;
!                         }
!                 }
          }
! 	lock_ReleaseWrite(&cm_scacheLock);
  
!         return code;
  }
  
  long cm_IoctlFlushFile(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	long code;
!         cm_scache_t *scp;
! 	cm_req_t req;
  
!         cm_InitReq(&req);
  
!         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
!         if (code) return code;
          
! 	cm_FlushFile(scp, userp, &req);
!         cm_ReleaseSCache(scp);
  
!         return 0;
  }
  
  long cm_IoctlSetVolumeStatus(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	cm_scache_t *scp;
! 	char volName[32];
! 	char offLineMsg[256];
! 	char motd[256];
! 	cm_conn_t *tcp;
! 	long code;
! 	AFSFetchVolumeStatus volStat;
! 	AFSStoreVolumeStatus storeStat;
! 	cm_volume_t *tvp;
! 	char *cp;
!         cm_cell_t *cellp;
! 	cm_req_t req;
  
! 	cm_InitReq(&req);
  
! 	code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
!         if (code) return code;
  
! 	cellp = cm_FindCellByID(scp->fid.cell);
!         osi_assert(cellp);
  
! 	if (scp->flags & CM_SCACHEFLAG_RO) {
!         	cm_ReleaseSCache(scp);
!         	return CM_ERROR_READONLY;
! 	}
  
! 	code = cm_GetVolumeByID(cellp, scp->fid.volume, userp, &req, &tvp);
!         if (code) {
! 		cm_ReleaseSCache(scp);
!         	return code;
! 	}
! 
! 	/* Copy the junk out, using cp as a roving pointer. */
! 	cp = ioctlp->inDatap;
! 	memcpy((char *)&volStat, cp, sizeof(AFSFetchVolumeStatus));
! 	cp += sizeof(AFSFetchVolumeStatus);
! 	strcpy(volName, cp);
! 	cp += strlen(volName)+1;
! 	strcpy(offLineMsg, cp);
! 	cp +=  strlen(offLineMsg)+1;
! 	strcpy(motd, cp);
! 	storeStat.Mask = 0;
! 	if (volStat.MinQuota != -1) {
! 		storeStat.MinQuota = volStat.MinQuota;
! 		storeStat.Mask |= AFS_SETMINQUOTA;
! 	}
! 	if (volStat.MaxQuota != -1) {
! 		storeStat.MaxQuota = volStat.MaxQuota;
! 		storeStat.Mask |= AFS_SETMAXQUOTA;
! 	}
! 
! 	do {
! 		code = cm_Conn(&scp->fid, userp, &req, &tcp);
! 		if (code) continue;
! 
! 		code = RXAFS_SetVolumeStatus(tcp->callp, scp->fid.volume,
! 			&storeStat, volName, offLineMsg, motd);
! 	} while (cm_Analyze(tcp, userp, &req, &scp->fid, NULL, NULL, NULL, code));
! 	code = cm_MapRPCError(code, &req);
! 
! 	/* return on failure */
! 	cm_ReleaseSCache(scp);
! 	if (code) {
!         	return code;
! 	}
! 
! 	/* we are sending parms back to make compat. with prev system.  should
! 	 * change interface later to not ask for current status, just set
!          * new status
!          */
! 	cp = ioctlp->outDatap;
! 	memcpy(cp, (char *)&volStat, sizeof(VolumeStatus));
! 	cp += sizeof(VolumeStatus);
! 	strcpy(cp, volName);
! 	cp += strlen(volName)+1;
! 	strcpy(cp, offLineMsg);
! 	cp += strlen(offLineMsg)+1;
! 	strcpy(cp, motd);
! 	cp += strlen(motd)+1;
  
! 	/* now return updated return data pointer */
! 	ioctlp->outDatap = cp;
  
! 	return 0;
! }
  
  long cm_IoctlGetVolumeStatus(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	char volName[32];
!         cm_scache_t *scp;
! 	char offLineMsg[256];
! 	char motd[256];
! 	cm_conn_t *tcp;
! 	register long code;
! 	AFSFetchVolumeStatus volStat;
! 	register char *cp;
! 	char *Name;
!         char *OfflineMsg;
!         char *MOTD;
! 	cm_req_t req;
  
! 	cm_InitReq(&req);
  
! 	code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
!         if (code) return code;
  
! 	Name = volName;
! 	OfflineMsg = offLineMsg;
! 	MOTD = motd;
! 	do {
! 		code = cm_Conn(&scp->fid, userp, &req, &tcp);
!                 if (code) continue;
! 
! 		code = RXAFS_GetVolumeStatus(tcp->callp, scp->fid.volume,
! 			&volStat, &Name, &OfflineMsg, &MOTD);
! 	} while (cm_Analyze(tcp, userp, &req, &scp->fid, NULL, NULL, NULL, code));
! 	code = cm_MapRPCError(code, &req);
! 
! 	cm_ReleaseSCache(scp);
! 	if (code) return code;
! 
!         /* Copy all this junk into msg->im_data, keeping track of the lengths. */
! 	cp = ioctlp->outDatap;
! 	memcpy(cp, (char *)&volStat, sizeof(AFSFetchVolumeStatus));
! 	cp += sizeof(AFSFetchVolumeStatus);
! 	strcpy(cp, volName);
! 	cp += strlen(volName)+1;
! 	strcpy(cp, offLineMsg);
! 	cp += strlen(offLineMsg)+1;
! 	strcpy(cp, motd);
! 	cp += strlen(motd)+1;
  
! 	/* return new size */
! 	ioctlp->outDatap = cp;
  
! 	return 0;
  }
  
  long cm_IoctlWhereIs(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	long code;
      cm_scache_t *scp;
      cm_cell_t *cellp;
      cm_volume_t *tvp;
! 	cm_serverRef_t **tsrpp, *current;
      cm_server_t *tsp;
      unsigned long volume;
      char *cp;
      cm_req_t req;
  
! 	cm_InitReq(&req);
  
      code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
      if (code) return code;
          
! 	volume = scp->fid.volume;
  
! 	cellp = cm_FindCellByID(scp->fid.cell);
      osi_assert(cellp);
  
      cm_ReleaseSCache(scp);
  
! 	code = cm_GetVolumeByID(cellp, volume, userp, &req, &tvp);
      if (code) return code;
  	
      cp = ioctlp->outDatap;
          
! 	lock_ObtainMutex(&tvp->mx);
! 	tsrpp = cm_GetVolServers(tvp, volume);
! 	lock_ObtainRead(&cm_serverLock);
! 	for (current = *tsrpp; current; current = current->next) {
! 		tsp = current->server;
! 		memcpy(cp, (char *)&tsp->addr.sin_addr.s_addr, sizeof(long));
! 		cp += sizeof(long);
! 	}
! 	lock_ReleaseRead(&cm_serverLock);
      cm_FreeServerList(tsrpp);
      lock_ReleaseMutex(&tvp->mx);
  
! 	/* still room for terminating NULL, add it on */
! 	volume = 0;	/* reuse vbl */
! 	memcpy(cp, (char *)&volume, sizeof(long));
! 	cp += sizeof(long);
! 
! 	ioctlp->outDatap = cp;
! 	cm_PutVolume(tvp);
! 	return 0;
! }
  
  long cm_IoctlStatMountPoint(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	long code;
!         cm_scache_t *dscp;
!         cm_scache_t *scp;
!         char *cp;
!         cm_req_t req;
  
! 	cm_InitReq(&req);
  
!         code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp);
!         if (code) return code;
!         
! 	cp = ioctlp->inDatap;
  
! 	code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
! 	cm_ReleaseSCache(dscp);
!         if (code) return code;
          
! 	lock_ObtainMutex(&scp->mx);
  
!         /* now check that this is a real mount point */
!         if (scp->fileType != CM_SCACHETYPE_MOUNTPOINT) {
! 		lock_ReleaseMutex(&scp->mx);
!         	cm_ReleaseSCache(scp);
!                 return CM_ERROR_INVAL;
!         }
! 	
!         code = cm_ReadMountPoint(scp, userp, &req);
!         if (code == 0) {
! 		cp = ioctlp->outDatap;
!                 strcpy(cp, scp->mountPointStringp);
!                 cp += strlen(cp) + 1;
!                 ioctlp->outDatap = cp;
!         }
! 	lock_ReleaseMutex(&scp->mx);
          cm_ReleaseSCache(scp);
  
! 	return code;
! }
  
  long cm_IoctlDeleteMountPoint(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	long code;
!         cm_scache_t *dscp;
!         cm_scache_t *scp;
!         char *cp;
!         cm_req_t req;
  
! 	cm_InitReq(&req);
  
!         code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp);
!         if (code) return code;
!         
! 	cp = ioctlp->inDatap;
  
! 	code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
          
! 	/* if something went wrong, bail out now */
!         if (code) {
! 		goto done;
! 	}
          
! 	lock_ObtainMutex(&scp->mx);
!         code = cm_SyncOp(scp, NULL, userp, &req, 0,
!         	CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
!         if (code) {
!         	lock_ReleaseMutex(&scp->mx);
!         	cm_ReleaseSCache(scp);
! 		goto done;
! 	}
! 	
!         /* now check that this is a real mount point */
!         if (scp->fileType != CM_SCACHETYPE_MOUNTPOINT) {
! 		lock_ReleaseMutex(&scp->mx);
!         	cm_ReleaseSCache(scp);
!                 code = CM_ERROR_INVAL;
!                 goto done;
!         }
! 	
!         /* time to make the RPC, so drop the lock */
! 	lock_ReleaseMutex(&scp->mx);
          cm_ReleaseSCache(scp);
!         
! 	/* easier to do it this way */
!         code = cm_Unlink(dscp, cp, userp, &req);
! 	if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
! 		smb_NotifyChange(FILE_ACTION_REMOVED,
! 				 FILE_NOTIFY_CHANGE_DIR_NAME,
! 				 dscp, cp, NULL, TRUE);
! 
! done:
! 	cm_ReleaseSCache(dscp);
! 	return code;
  }
  
  long cm_IoctlCheckServers(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	cm_cell_t *cellp;
!         chservinfo_t csi;
!         char *tp;
!         char *cp;
!         long temp;
!         cm_server_t *tsp;
!         int haveCell;
!         
! 	cm_SkipIoctlPath(ioctlp);	/* we don't care about the path */
! 	tp = ioctlp->inDatap;
!         haveCell = 0;
! 
! 	memcpy(&temp, tp, sizeof(temp));
! 	if (temp == 0x12345678) {	/* For afs3.3 version */
!                 memcpy(&csi, tp, sizeof(csi));
! 		if (csi.tinterval >= 0) {
! 			cp = ioctlp->outDatap;
! 			memcpy(cp, (char *)&cm_daemonCheckInterval, sizeof(long));
! 			ioctlp->outDatap += sizeof(long);
! 			if (csi.tinterval > 0) {
! 				if (!smb_SUser(userp))
! 					return CM_ERROR_NOACCESS;
! 				cm_daemonCheckInterval = csi.tinterval;
! 			}
! 			return 0;
! 		}
! 		if (csi.tsize)
! 			haveCell = 1;
! 		temp = csi.tflags;
! 		cp = csi.tbuffer;
! 	} else {	/* For pre afs3.3 versions */
! 		memcpy((char *)&temp, ioctlp->inDatap, sizeof(long));
! 		ioctlp->inDatap = cp = ioctlp->inDatap + sizeof(long);
! 		if (cp - ioctlp->inAllocp < ioctlp->inCopied)	/* still more data available */
! 			haveCell = 1;
! 	}
! 
! 	/* 
! 	 * 1: fast check, don't contact servers.
! 	 * 2: local cell only.
! 	 */
! 	if (haveCell) {
! 		/* have cell name, too */
! 		cellp = cm_GetCell(cp, 0);
! 		if (!cellp) return CM_ERROR_NOSUCHCELL;
! 	}
! 	else cellp = (cm_cell_t *) 0;
! 	if (!cellp && (temp & 2)) {
! 		/* use local cell */
! 		cellp = cm_FindCellByID(1);
! 	}
! 	if (!(temp & 1)) {	/* if not fast, call server checker routine */
! 		/* check down servers */
! 		cm_CheckServers(CM_FLAG_CHECKDOWNSERVERS | CM_FLAG_CHECKUPSERVERS,
!                 	cellp);
! 	}
! 
! 	/* now return the current down server list */
! 	cp = ioctlp->outDatap;
! 	lock_ObtainRead(&cm_serverLock);
! 	for(tsp = cm_allServersp; tsp; tsp=tsp->allNextp) {
! 		if (cellp && tsp->cellp != cellp) continue;	/* cell spec'd and wrong */
! 		if ((tsp->flags & CM_SERVERFLAG_DOWN)
!                 	&& tsp->type == CM_SERVER_FILE) {
! 			memcpy(cp, (char *)&tsp->addr.sin_addr.s_addr, sizeof(long));
! 			cp += sizeof(long);
! 		}
! 	}
! 	lock_ReleaseRead(&cm_serverLock);
  
! 	ioctlp->outDatap = cp;
! 	return 0;
  }
  
  long cm_IoctlGag(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	/* we don't print anything superfluous, so we don't support the gag call */
! 	return CM_ERROR_INVAL;
  }
  
  long cm_IoctlCheckVolumes(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	cm_CheckVolumes();
! 	return 0;
! }
  
  long cm_IoctlSetCacheSize(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	long temp;
!         long code;
!         
! 	cm_SkipIoctlPath(ioctlp);
  
!         memcpy(&temp, ioctlp->inDatap, sizeof(temp));
!         if (temp == 0) temp = buf_nOrigBuffers;
!         else {
! 		/* temp is in 1K units, convert to # of buffers */
!                 temp = temp / (buf_bufferSize / 1024);
!         }
  
! 	/* now adjust the cache size */
!         code = buf_SetNBuffers(temp);
  
! 	return code;
  }
  
  long cm_IoctlTraceControl(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!         long inValue;
          
!         cm_SkipIoctlPath(ioctlp);
          
!         memcpy(&inValue, ioctlp->inDatap, sizeof(long));
  
! 	/* print trace */
! 	if (inValue & 8) {
! 		afsd_ForceTrace(FALSE);
! 	}
!         
!         if (inValue & 2) {
!         	/* set tracing value to low order bit */
!         	if ((inValue & 1) == 0) {
! 			/* disable tracing */
! 	                osi_LogDisable(afsd_logp);
! 	        }
! 	        else {
! 			/* enable tracing */
! 	        	osi_LogEnable(afsd_logp);
! 		}
! 	}
! 
! 	/* see if we're supposed to do a reset, too */
!         if (inValue & 4) {
!         	osi_LogReset(afsd_logp);
! 	}
! 
!         /* and copy out tracing flag */
!         inValue = afsd_logp->enabled;	/* use as a temp vbl */
!         memcpy(ioctlp->outDatap, &inValue, sizeof(long));
!         ioctlp->outDatap += sizeof(long);
!         return 0;
! }
  
  long cm_IoctlGetCacheParms(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	cm_cacheParms_t parms;
!         
!         memset(&parms, 0, sizeof(parms));
  
! 	/* first we get, in 1K units, the cache size */
!         parms.parms[0] = buf_nbuffers * (buf_bufferSize / 1024);
!         
!         /* and then the actual # of buffers in use (not in the free list, I guess,
!          * will be what we do).
!          */
!         parms.parms[1] = (buf_nbuffers - buf_CountFreeList()) * (buf_bufferSize / 1024);
!         
!         memcpy(ioctlp->outDatap, &parms, sizeof(parms));
!         ioctlp->outDatap += sizeof(parms);
  
! 	return 0;
  }
  
  long cm_IoctlGetCell(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	long whichCell;
      long magic = 0;
! 	cm_cell_t *tcellp;
! 	cm_serverRef_t *serverRefp;
      cm_server_t *serverp;
! 	long i;
      char *cp;
      char *tp;
      char *basep;
  
! 	cm_SkipIoctlPath(ioctlp);
  
! 	tp = ioctlp->inDatap;
  
! 	memcpy((char *)&whichCell, tp, sizeof(long));
! 	tp += sizeof(long);
! 	
! 	/* see if more than one long passed in, ignoring the null pathname (the -1) */
! 	if (ioctlp->inCopied-1 > sizeof(long)) {
! 		memcpy((char *)&magic, tp, sizeof(long));
! 	}
  
      lock_ObtainRead(&cm_cellLock);
! 	for(tcellp = cm_allCellsp; tcellp; tcellp = tcellp->nextp) {
! 		if (whichCell == 0) break;
! 		whichCell--;
! 	}
! 	lock_ReleaseRead(&cm_cellLock);
! 	if (tcellp) {
! 		int max = 8;
! 
! 		cp = ioctlp->outDatap;
! 
! 		if (magic == 0x12345678) {
! 			memcpy(cp, (char *)&magic, sizeof(long));
! 			max = 13;
! 		}
! 		memset(cp, 0, max * sizeof(long));
          basep = cp;
! 		lock_ObtainRead(&cm_serverLock);	/* for going down server list */
          /* jaltman - do the reference counts to serverRefp contents need to be increased? */
! 		serverRefp = tcellp->vlServersp;
! 		for(i=0; i<max; i++) {
! 			if (!serverRefp) break;
! 			serverp = serverRefp->server;
! 			memcpy(cp, &serverp->addr.sin_addr.s_addr, sizeof(long));
! 			cp += sizeof(long);
              serverRefp = serverRefp->next;
! 		}
! 		lock_ReleaseRead(&cm_serverLock);
! 		cp = basep + max * sizeof(afs_int32);
! 		strcpy(cp, tcellp->namep);
! 		cp += strlen(tcellp->namep)+1;
! 		ioctlp->outDatap = cp;
! 	}
  
      if (tcellp) 
          return 0;
--- 352,1091 ----
              free(sharePath);
              if (code) return code;
  
!             code = cm_NameI(substRootp, p, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
!                              userp, NULL, reqp, scpp);
!             if (code) return code;
          } else {
              /* otherwise, treat the name as a cellname mounted off the afs root.
!              * This requires that we reconstruct the shareName string with 
!              * leading and trailing slashes.
!              */
              p = tbuffer + 2 + strlen(cm_NetbiosName) + 1;
!             if ( !_strnicmp("all", p, 3) )
!                 p += 4;
  
!             shareName[0] = '/';
!             for (i = 1; *p && *p != '\\'; i++,p++ ) {
!                 shareName[i] = *p;
!             }
!             p++;                    /* skip past trailing slash */
!             shareName[i++] = '/';	/* add trailing slash */
!             shareName[i] = 0;       /* terminate string */
! 
!             code = cm_NameI(cm_rootSCachep, ioctlp->prefix->data,
                               CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
                               userp, shareName, reqp, &substRootp);
              if (code) return code;
  
!             code = cm_NameI(substRootp, p, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
!                             userp, NULL, reqp, scpp);
!             if (code) return code;
          }
      } else {
          code = cm_NameI(cm_rootSCachep, ioctlp->prefix->data,
!                         CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
!                         userp, ioctlp->tidPathp, reqp, &substRootp);
          if (code) return code;
  
          code = cm_NameI(substRootp, tbuffer, CM_FLAG_FOLLOW,
!                         userp, NULL, reqp, scpp);
          if (code) return code;
      }
  
!     /* # of bytes of path */
!     code = strlen(ioctlp->inDatap) + 1;
!     ioctlp->inDatap += code;
  
!     /* and return success */
!     return 0;
  }
  
  long cm_IoctlGetACL(smb_ioctl_t *ioctlp, cm_user_t *userp)
  {
!     cm_conn_t *connp;
!     cm_scache_t *scp;
!     AFSOpaque acl;
!     AFSFetchStatus fileStatus;
!     AFSVolSync volSync;
!     long code;
!     AFSFid fid;
!     int tlen;
!     cm_req_t req;
!     struct rx_connection * callp;
  
!     cm_InitReq(&req);
  
!     code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
!     if (code) return code;
  
!     /* now make the get acl call */
!     fid.Volume = scp->fid.volume;
!     fid.Vnode = scp->fid.vnode;
!     fid.Unique = scp->fid.unique;
!     do {
!         acl.AFSOpaque_val = ioctlp->outDatap;
!         acl.AFSOpaque_len = 0;
!         code = cm_Conn(&scp->fid, userp, &req, &connp);
!         if (code) continue;
! 
!         callp = cm_GetRxConn(connp);
!         code = RXAFS_FetchACL(callp, &fid, &acl, &fileStatus, &volSync);
!         rx_PutConnection(callp);
! 
!     } while (cm_Analyze(connp, userp, &req, &scp->fid, &volSync, NULL, NULL, code));
!     code = cm_MapRPCError(code, &req);
!     cm_ReleaseSCache(scp);
! 
!     if (code) return code;
! 
!     /* skip over return data */
!     tlen = strlen(ioctlp->outDatap) + 1;
!     ioctlp->outDatap += tlen;
! 
!     /* and return success */
!     return 0;
  }
  
  long cm_IoctlGetFileCellName(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     long code;
!     cm_scache_t *scp;
!     cm_cell_t *cellp;
!     cm_req_t req;
  
!     cm_InitReq(&req);
  
!     code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
!     if (code) return code;
! 
!     cellp = cm_FindCellByID(scp->fid.cell);
!     if (cellp) {
!         strcpy(ioctlp->outDatap, cellp->namep);
!         ioctlp->outDatap += strlen(ioctlp->outDatap) + 1;
!         code = 0;
!     }
!     else code = CM_ERROR_NOSUCHCELL;
! 
!     cm_ReleaseSCache(scp);
!     return code;
  }
  
  long cm_IoctlSetACL(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     cm_conn_t *connp;
!     cm_scache_t *scp;
!     AFSOpaque acl;
!     AFSFetchStatus fileStatus;
!     AFSVolSync volSync;
!     long code;
!     AFSFid fid;
!     cm_req_t req;
!     struct rx_connection * callp;
  
!     cm_InitReq(&req);
  
!     code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
!     if (code) return code;
  	
!     /* now make the get acl call */
!     fid.Volume = scp->fid.volume;
!     fid.Vnode = scp->fid.vnode;
!     fid.Unique = scp->fid.unique;
!     do {
!         acl.AFSOpaque_val = ioctlp->inDatap;
!         acl.AFSOpaque_len = strlen(ioctlp->inDatap)+1;
!         code = cm_Conn(&scp->fid, userp, &req, &connp);
!         if (code) continue;
! 
!         callp = cm_GetRxConn(connp);
!         code = RXAFS_StoreACL(callp, &fid, &acl, &fileStatus, &volSync);
!         rx_PutConnection(callp);
! 
!     } while (cm_Analyze(connp, userp, &req, &scp->fid, &volSync, NULL, NULL, code));
!     code = cm_MapRPCError(code, &req);
! 
!     /* invalidate cache info, since we just trashed the ACL cache */
!     lock_ObtainMutex(&scp->mx);
!     cm_DiscardSCache(scp);
!     lock_ReleaseMutex(&scp->mx);
  
!     cm_ReleaseSCache(scp);
! 
!     return code;
  }
  
  long cm_IoctlFlushVolume(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     long code;
!     cm_scache_t *scp;
!     unsigned long volume;
!     int i;
!     cm_req_t req;
  
!     cm_InitReq(&req);
  
!     code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
!     if (code) return code;
          
!     volume = scp->fid.volume;
!     cm_ReleaseSCache(scp);
  
!     lock_ObtainWrite(&cm_scacheLock);
!     for (i=0; i<cm_hashTableSize; i++) {
!         for (scp = cm_hashTablep[i]; scp; scp = scp->nextp) {
!             if (scp->fid.volume == volume) {
!                 scp->refCount++;
!                 lock_ReleaseWrite(&cm_scacheLock);
! 
!                 /* now flush the file */
!                 code = cm_FlushFile(scp, userp, &req);
!                 if ( code )
!                     afsi_log("cm_FlushFile returns error: [%x]",code);
!                 lock_ObtainWrite(&cm_scacheLock);
!                 scp->refCount--;
!             }
          }
!     }
!     lock_ReleaseWrite(&cm_scacheLock);
  
!     return code;
  }
  
  long cm_IoctlFlushFile(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     long code;
!     cm_scache_t *scp;
!     cm_req_t req;
  
!     cm_InitReq(&req);
  
!     code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
!     if (code) return code;
          
!     cm_FlushFile(scp, userp, &req);
!     cm_ReleaseSCache(scp);
  
!     return 0;
  }
  
  long cm_IoctlSetVolumeStatus(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     cm_scache_t *scp;
!     char volName[32];
!     char offLineMsg[256];
!     char motd[256];
!     cm_conn_t *tcp;
!     long code;
!     AFSFetchVolumeStatus volStat;
!     AFSStoreVolumeStatus storeStat;
!     cm_volume_t *tvp;
!     char *cp;
!     cm_cell_t *cellp;
!     cm_req_t req;
!     struct rx_connection * callp;
  
!     cm_InitReq(&req);
  
!     code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
!     if (code) return code;
  
!     cellp = cm_FindCellByID(scp->fid.cell);
!     osi_assert(cellp);
  
!     if (scp->flags & CM_SCACHEFLAG_RO) {
!         cm_ReleaseSCache(scp);
!         return CM_ERROR_READONLY;
!     }
  
!     code = cm_GetVolumeByID(cellp, scp->fid.volume, userp, &req, &tvp);
!     if (code) {
!         cm_ReleaseSCache(scp);
!         return code;
!     }
  
!     /* Copy the junk out, using cp as a roving pointer. */
!     cp = ioctlp->inDatap;
!     memcpy((char *)&volStat, cp, sizeof(AFSFetchVolumeStatus));
!     cp += sizeof(AFSFetchVolumeStatus);
!     strcpy(volName, cp);
!     cp += strlen(volName)+1;
!     strcpy(offLineMsg, cp);
!     cp +=  strlen(offLineMsg)+1;
!     strcpy(motd, cp);
!     storeStat.Mask = 0;
!     if (volStat.MinQuota != -1) {
!         storeStat.MinQuota = volStat.MinQuota;
!         storeStat.Mask |= AFS_SETMINQUOTA;
!     }
!     if (volStat.MaxQuota != -1) {
!         storeStat.MaxQuota = volStat.MaxQuota;
!         storeStat.Mask |= AFS_SETMAXQUOTA;
!     }
  
!     do {
!         code = cm_Conn(&scp->fid, userp, &req, &tcp);
!         if (code) continue;
! 
!         callp = cm_GetRxConn(tcp);
!         code = RXAFS_SetVolumeStatus(callp, scp->fid.volume,
!                                       &storeStat, volName, offLineMsg, motd);
!         rx_PutConnection(callp);
! 
!     } while (cm_Analyze(tcp, userp, &req, &scp->fid, NULL, NULL, NULL, code));
!     code = cm_MapRPCError(code, &req);
! 
!     /* return on failure */
!     cm_ReleaseSCache(scp);
!     if (code) {
!         return code;
!     }
! 
!     /* we are sending parms back to make compat. with prev system.  should
!      * change interface later to not ask for current status, just set
!      * new status
!      */
!     cp = ioctlp->outDatap;
!     memcpy(cp, (char *)&volStat, sizeof(VolumeStatus));
!     cp += sizeof(VolumeStatus);
!     strcpy(cp, volName);
!     cp += strlen(volName)+1;
!     strcpy(cp, offLineMsg);
!     cp += strlen(offLineMsg)+1;
!     strcpy(cp, motd);
!     cp += strlen(motd)+1;
! 
!     /* now return updated return data pointer */
!     ioctlp->outDatap = cp;
! 
!     return 0;
! }       
  
  long cm_IoctlGetVolumeStatus(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     char volName[32];
!     cm_scache_t *scp;
!     char offLineMsg[256];
!     char motd[256];
!     cm_conn_t *tcp;
!     register long code;
!     AFSFetchVolumeStatus volStat;
!     register char *cp;
!     char *Name;
!     char *OfflineMsg;
!     char *MOTD;
!     cm_req_t req;
!     struct rx_connection * callp;
  
!     cm_InitReq(&req);
  
!     code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
!     if (code) return code;
! 
!     Name = volName;
!     OfflineMsg = offLineMsg;
!     MOTD = motd;
!     do {
!         code = cm_Conn(&scp->fid, userp, &req, &tcp);
!         if (code) continue;
! 
!         callp = cm_GetRxConn(tcp);
!         code = RXAFS_GetVolumeStatus(callp, scp->fid.volume,
!                                       &volStat, &Name, &OfflineMsg, &MOTD);
!         rx_PutConnection(callp);
! 
!     } while (cm_Analyze(tcp, userp, &req, &scp->fid, NULL, NULL, NULL, code));
!     code = cm_MapRPCError(code, &req);
  
!     cm_ReleaseSCache(scp);
!     if (code) return code;
! 
!     /* Copy all this junk into msg->im_data, keeping track of the lengths. */
!     cp = ioctlp->outDatap;
!     memcpy(cp, (char *)&volStat, sizeof(AFSFetchVolumeStatus));
!     cp += sizeof(AFSFetchVolumeStatus);
!     strcpy(cp, volName);
!     cp += strlen(volName)+1;
!     strcpy(cp, offLineMsg);
!     cp += strlen(offLineMsg)+1;
!     strcpy(cp, motd);
!     cp += strlen(motd)+1;
  
!     /* return new size */
!     ioctlp->outDatap = cp;
  
!     return 0;
  }
  
  long cm_IoctlWhereIs(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     long code;
      cm_scache_t *scp;
      cm_cell_t *cellp;
      cm_volume_t *tvp;
!     cm_serverRef_t **tsrpp, *current;
      cm_server_t *tsp;
      unsigned long volume;
      char *cp;
      cm_req_t req;
  
!     cm_InitReq(&req);
  
      code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
      if (code) return code;
          
!     volume = scp->fid.volume;
  
!     cellp = cm_FindCellByID(scp->fid.cell);
      osi_assert(cellp);
  
      cm_ReleaseSCache(scp);
  
!     code = cm_GetVolumeByID(cellp, volume, userp, &req, &tvp);
      if (code) return code;
  	
      cp = ioctlp->outDatap;
          
!     lock_ObtainMutex(&tvp->mx);
!     tsrpp = cm_GetVolServers(tvp, volume);
!     lock_ObtainRead(&cm_serverLock);
!     for (current = *tsrpp; current; current = current->next) {
!         tsp = current->server;
!         memcpy(cp, (char *)&tsp->addr.sin_addr.s_addr, sizeof(long));
!         cp += sizeof(long);
!     }
!     lock_ReleaseRead(&cm_serverLock);
      cm_FreeServerList(tsrpp);
      lock_ReleaseMutex(&tvp->mx);
  
!     /* still room for terminating NULL, add it on */
!     volume = 0;	/* reuse vbl */
!     memcpy(cp, (char *)&volume, sizeof(long));
!     cp += sizeof(long);
! 
!     ioctlp->outDatap = cp;
!     cm_PutVolume(tvp);
!     return 0;
! }       
  
  long cm_IoctlStatMountPoint(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     long code;
!     cm_scache_t *dscp;
!     cm_scache_t *scp;
!     char *cp;
!     cm_req_t req;
  
!     cm_InitReq(&req);
  
!     code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp);
!     if (code) return code;
  
!     cp = ioctlp->inDatap;
! 
!     code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
!     cm_ReleaseSCache(dscp);
!     if (code) return code;
          
!     lock_ObtainMutex(&scp->mx);
  
!     /* now check that this is a real mount point */
!     if (scp->fileType != CM_SCACHETYPE_MOUNTPOINT) {
!         lock_ReleaseMutex(&scp->mx);
          cm_ReleaseSCache(scp);
+         return CM_ERROR_INVAL;
+     }
  
!     code = cm_ReadMountPoint(scp, userp, &req);
!     if (code == 0) {
!         cp = ioctlp->outDatap;
!         strcpy(cp, scp->mountPointStringp);
!         cp += strlen(cp) + 1;
!         ioctlp->outDatap = cp;
!     }
!     lock_ReleaseMutex(&scp->mx);
!     cm_ReleaseSCache(scp);
! 
!     return code;
! }       
  
  long cm_IoctlDeleteMountPoint(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     long code;
!     cm_scache_t *dscp;
!     cm_scache_t *scp;
!     char *cp;
!     cm_req_t req;
  
!     cm_InitReq(&req);
  
!     code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp);
!     if (code) return code;
! 
!     cp = ioctlp->inDatap;
  
!     code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
          
!     /* if something went wrong, bail out now */
!     if (code) {
!         goto done;
!     }
          
!     lock_ObtainMutex(&scp->mx);
!     code = cm_SyncOp(scp, NULL, userp, &req, 0,
!                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
!     if (code) {     
!         lock_ReleaseMutex(&scp->mx);
          cm_ReleaseSCache(scp);
!         goto done;
!     }
! 
!     /* now check that this is a real mount point */
!     if (scp->fileType != CM_SCACHETYPE_MOUNTPOINT) {
!         lock_ReleaseMutex(&scp->mx);
!         cm_ReleaseSCache(scp);
!         code = CM_ERROR_INVAL;
!         goto done;
!     }
! 
!     /* time to make the RPC, so drop the lock */
!     lock_ReleaseMutex(&scp->mx);
!     cm_ReleaseSCache(scp);
! 
!     /* easier to do it this way */
!     code = cm_Unlink(dscp, cp, userp, &req);
!     if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
!         smb_NotifyChange(FILE_ACTION_REMOVED,
!                           FILE_NOTIFY_CHANGE_DIR_NAME,
!                           dscp, cp, NULL, TRUE);
! 
!   done:
!     cm_ReleaseSCache(dscp);
!     return code;
  }
  
  long cm_IoctlCheckServers(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     cm_cell_t *cellp;
!     chservinfo_t csi;
!     char *tp;
!     char *cp;
!     long temp;
!     cm_server_t *tsp;
!     int haveCell;
!         
!     cm_SkipIoctlPath(ioctlp);	/* we don't care about the path */
!     tp = ioctlp->inDatap;
!     haveCell = 0;
! 
!     memcpy(&temp, tp, sizeof(temp));
!     if (temp == 0x12345678) {	/* For afs3.3 version */
!         memcpy(&csi, tp, sizeof(csi));
!         if (csi.tinterval >= 0) {
!             cp = ioctlp->outDatap;
!             memcpy(cp, (char *)&cm_daemonCheckInterval, sizeof(long));
!             ioctlp->outDatap += sizeof(long);
!             if (csi.tinterval > 0) {
!                 if (!smb_SUser(userp))
!                     return CM_ERROR_NOACCESS;
!                 cm_daemonCheckInterval = csi.tinterval;
!             }
!             return 0;
!         }
!         if (csi.tsize)
!             haveCell = 1;
!         temp = csi.tflags;
!         cp = csi.tbuffer;
!     } else {	/* For pre afs3.3 versions */
!         memcpy((char *)&temp, ioctlp->inDatap, sizeof(long));
!         ioctlp->inDatap = cp = ioctlp->inDatap + sizeof(long);
!         if (cp - ioctlp->inAllocp < ioctlp->inCopied)	/* still more data available */
!             haveCell = 1;
!     }       
! 
!     /* 
!      * 1: fast check, don't contact servers.
!      * 2: local cell only.
!      */
!     if (haveCell) {
!         /* have cell name, too */
!         cellp = cm_GetCell(cp, 0);
!         if (!cellp) return CM_ERROR_NOSUCHCELL;
!     }
!     else cellp = (cm_cell_t *) 0;
!     if (!cellp && (temp & 2)) {
!         /* use local cell */
!         cellp = cm_FindCellByID(1);
!     }
!     if (!(temp & 1)) {	/* if not fast, call server checker routine */
!         /* check down servers */
!         cm_CheckServers(CM_FLAG_CHECKDOWNSERVERS | CM_FLAG_CHECKUPSERVERS,
!                          cellp);
!     }       
! 
!     /* now return the current down server list */
!     cp = ioctlp->outDatap;
!     lock_ObtainRead(&cm_serverLock);
!     for (tsp = cm_allServersp; tsp; tsp=tsp->allNextp) {
!         if (cellp && tsp->cellp != cellp) continue;	/* cell spec'd and wrong */
!         if ((tsp->flags & CM_SERVERFLAG_DOWN)
!              && tsp->type == CM_SERVER_FILE) {
!             memcpy(cp, (char *)&tsp->addr.sin_addr.s_addr, sizeof(long));
!             cp += sizeof(long);
!         }
!     }
!     lock_ReleaseRead(&cm_serverLock);
  
!     ioctlp->outDatap = cp;
!     return 0;
  }
  
  long cm_IoctlGag(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     /* we don't print anything superfluous, so we don't support the gag call */
!     return CM_ERROR_INVAL;
  }
  
  long cm_IoctlCheckVolumes(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     cm_CheckVolumes();
!     return 0;
! }       
  
  long cm_IoctlSetCacheSize(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     long temp;
!     long code;
  
!     cm_SkipIoctlPath(ioctlp);
  
!     memcpy(&temp, ioctlp->inDatap, sizeof(temp));
!     if (temp == 0) 
!         temp = buf_nOrigBuffers;
!     else {
!         /* temp is in 1K units, convert to # of buffers */
!         temp = temp / (buf_bufferSize / 1024);
!     }       
  
!     /* now adjust the cache size */
!     code = buf_SetNBuffers(temp);
! 
!     return code;
  }
  
  long cm_IoctlTraceControl(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     long inValue;
!         
!     cm_SkipIoctlPath(ioctlp);
          
!     memcpy(&inValue, ioctlp->inDatap, sizeof(long));
! 
!     /* print trace */
!     if (inValue & 8) {
!         afsd_ForceTrace(FALSE);
!     }
          
!     if (inValue & 2) {
!         /* set tracing value to low order bit */
!         if ((inValue & 1) == 0) {
!             /* disable tracing */
!             osi_LogDisable(afsd_logp);
!         }
!         else {
!             /* enable tracing */
!             osi_LogEnable(afsd_logp);
!         }
!     }
  
!     /* see if we're supposed to do a reset, too */
!     if (inValue & 4) {
!         osi_LogReset(afsd_logp);
!     }
! 
!     /* and copy out tracing flag */
!     inValue = afsd_logp->enabled;	/* use as a temp vbl */
!     memcpy(ioctlp->outDatap, &inValue, sizeof(long));
!     ioctlp->outDatap += sizeof(long);
!     return 0;
! }       
  
  long cm_IoctlGetCacheParms(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     cm_cacheParms_t parms;
  
!     memset(&parms, 0, sizeof(parms));
! 
!     /* first we get, in 1K units, the cache size */
!     parms.parms[0] = buf_nbuffers * (buf_bufferSize / 1024);
! 
!     /* and then the actual # of buffers in use (not in the free list, I guess,
!      * will be what we do).
!      */
!     parms.parms[1] = (buf_nbuffers - buf_CountFreeList()) * (buf_bufferSize / 1024);
! 
!     memcpy(ioctlp->outDatap, &parms, sizeof(parms));
!     ioctlp->outDatap += sizeof(parms);
  
!     return 0;
  }
  
  long cm_IoctlGetCell(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     long whichCell;
      long magic = 0;
!     cm_cell_t *tcellp;
!     cm_serverRef_t *serverRefp;
      cm_server_t *serverp;
!     long i;
      char *cp;
      char *tp;
      char *basep;
  
!     cm_SkipIoctlPath(ioctlp);
  
!     tp = ioctlp->inDatap;
  
!     memcpy((char *)&whichCell, tp, sizeof(long));
!     tp += sizeof(long);
! 
!     /* see if more than one long passed in, ignoring the null pathname (the -1) */
!     if (ioctlp->inCopied-1 > sizeof(long)) {
!         memcpy((char *)&magic, tp, sizeof(long));
!     }
  
      lock_ObtainRead(&cm_cellLock);
!     for (tcellp = cm_allCellsp; tcellp; tcellp = tcellp->nextp) {
!         if (whichCell == 0) break;
!         whichCell--;
!     }
!     lock_ReleaseRead(&cm_cellLock);
!     if (tcellp) {
!         int max = 8;
! 
!         cp = ioctlp->outDatap;
! 
!         if (magic == 0x12345678) {
!             memcpy(cp, (char *)&magic, sizeof(long));
!             max = 13;
!         }
!         memset(cp, 0, max * sizeof(long));
          basep = cp;
!         lock_ObtainRead(&cm_serverLock);	/* for going down server list */
          /* jaltman - do the reference counts to serverRefp contents need to be increased? */
!         serverRefp = tcellp->vlServersp;
!         for (i=0; i<max; i++) {
!             if (!serverRefp) break;
!             serverp = serverRefp->server;
!             memcpy(cp, &serverp->addr.sin_addr.s_addr, sizeof(long));
!             cp += sizeof(long);
              serverRefp = serverRefp->next;
!         }
!         lock_ReleaseRead(&cm_serverLock);
!         cp = basep + max * sizeof(afs_int32);
!         strcpy(cp, tcellp->namep);
!         cp += strlen(tcellp->namep)+1;
!         ioctlp->outDatap = cp;
!     }
  
      if (tcellp) 
          return 0;
***************
*** 1077,1090 ****
       * are already loaded.
    
       * cell list will be cm_CellLock and cm_ServerLock will be held for write.
!     */  
    
      cm_cell_t *cp;
    
      cm_SkipIoctlPath(ioctlp);
      lock_ObtainWrite(&cm_cellLock);
    
!     for(cp = cm_allCellsp; cp; cp=cp->nextp) 
      {
          long code;
          /* delete all previous server lists - cm_FreeServerList will ask for write on cm_ServerLock*/
--- 1104,1117 ----
       * are already loaded.
    
       * cell list will be cm_CellLock and cm_ServerLock will be held for write.
!      */  
    
      cm_cell_t *cp;
    
      cm_SkipIoctlPath(ioctlp);
      lock_ObtainWrite(&cm_cellLock);
    
!     for (cp = cm_allCellsp; cp; cp=cp->nextp) 
      {
          long code;
          /* delete all previous server lists - cm_FreeServerList will ask for write on cm_ServerLock*/
***************
*** 1092,1098 ****
          cp->vlServersp = NULL;
          code = cm_SearchCellFile(cp->namep, cp->namep, cm_AddCellProc, cp);
  #ifdef AFS_AFSDB_ENV
! 		if (code) {
              if (cm_dnsEnabled) {
                  int ttl;
                  code = cm_SearchCellByDNS(cp->namep, cp->namep, &ttl, cm_AddCellProc, cp);
--- 1119,1125 ----
          cp->vlServersp = NULL;
          code = cm_SearchCellFile(cp->namep, cp->namep, cm_AddCellProc, cp);
  #ifdef AFS_AFSDB_ENV
!         if (code) {
              if (cm_dnsEnabled) {
                  int ttl;
                  code = cm_SearchCellByDNS(cp->namep, cp->namep, &ttl, cm_AddCellProc, cp);
***************
*** 1122,1148 ****
  
  long cm_IoctlGetWsCell(smb_ioctl_t *ioctlp, cm_user_t *userp)
  {
! 	/* if we don't know our default cell, return failure */
! 	if (cm_rootCellp == NULL) {
! 		return CM_ERROR_NOSUCHCELL;
! 	}
! 
! 	/* return the default cellname to the caller */
! 	strcpy(ioctlp->outDatap, cm_rootCellp->namep);
! 	ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
!         
! 	/* done: success */
!         return 0;
  }
  
  long cm_IoctlSysName(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	long setSysName, foundname = 0;
      char *cp, *cp2, inname[MAXSYSNAME], outname[MAXSYSNAME];
      int t, count, num = 0;
      char **sysnamelist[MAXSYSNAME];
          
! 	cm_SkipIoctlPath(ioctlp);
  
      memcpy(&setSysName, ioctlp->inDatap, sizeof(long));
      ioctlp->inDatap += sizeof(long);
--- 1149,1175 ----
  
  long cm_IoctlGetWsCell(smb_ioctl_t *ioctlp, cm_user_t *userp)
  {
!     /* if we don't know our default cell, return failure */
!     if (cm_rootCellp == NULL) {
!         return CM_ERROR_NOSUCHCELL;
!     }
! 
!     /* return the default cellname to the caller */
!     strcpy(ioctlp->outDatap, cm_rootCellp->namep);
!     ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
! 
!     /* done: success */
!     return 0;
  }
  
  long cm_IoctlSysName(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     long setSysName, foundname = 0;
      char *cp, *cp2, inname[MAXSYSNAME], outname[MAXSYSNAME];
      int t, count, num = 0;
      char **sysnamelist[MAXSYSNAME];
          
!     cm_SkipIoctlPath(ioctlp);
  
      memcpy(&setSysName, ioctlp->inDatap, sizeof(long));
      ioctlp->inDatap += sizeof(long);
***************
*** 1180,1186 ****
          strcpy(outname, cm_sysName);
          foundname = cm_sysNameCount;
          *sysnamelist = cm_sysNameList;
!     } else {                /* Local guy; only root can change sysname */
          /* clear @sys entries from the dnlc, once afs_lookup can
           * do lookups of @sys entries and thinks it can trust them */
          /* privs ok, store the entry, ... */
--- 1207,1214 ----
          strcpy(outname, cm_sysName);
          foundname = cm_sysNameCount;
          *sysnamelist = cm_sysNameList;
!     } else {        
!         /* Local guy; only root can change sysname */
          /* clear @sys entries from the dnlc, once afs_lookup can
           * do lookups of @sys entries and thinks it can trust them */
          /* privs ok, store the entry, ... */
***************
*** 1189,1197 ****
              cp = ioctlp->inDatap;
              for (count = 1; count < setSysName; ++count) {
                  if (!cm_sysNameList[count])
!                     osi_panic
!                         ("cm_IoctlSysName: no cm_sysNameList entry to write\n"
!                           , __FILE__, __LINE__);
                  t = strlen(cp);
                  memcpy(cm_sysNameList[count], cp, t + 1);  /* include null */
                  cp += t + 1;
--- 1217,1224 ----
              cp = ioctlp->inDatap;
              for (count = 1; count < setSysName; ++count) {
                  if (!cm_sysNameList[count])
!                     osi_panic("cm_IoctlSysName: no cm_sysNameList entry to write\n",
!                                __FILE__, __LINE__);
                  t = strlen(cp);
                  memcpy(cm_sysNameList[count], cp, t + 1);  /* include null */
                  cp += t + 1;
***************
*** 1201,1208 ****
      }
  
      if (!setSysName) {
! 		/* return the sysname to the caller */
! 		cp = ioctlp->outDatap;
          memcpy(cp, (char *)&foundname, sizeof(afs_int32));
          cp += sizeof(afs_int32);	/* skip found flag */
          if (foundname) {
--- 1228,1235 ----
      }
  
      if (!setSysName) {
!         /* return the sysname to the caller */
!         cp = ioctlp->outDatap;
          memcpy(cp, (char *)&foundname, sizeof(afs_int32));
          cp += sizeof(afs_int32);	/* skip found flag */
          if (foundname) {
***************
*** 1210,1221 ****
              cp += strlen(outname) + 1;	/* skip name and terminating null char */
              for ( count=1; count < foundname ; ++count) {   /* ... or list */
                  if ( !(*sysnamelist)[count] )
!                     osi_panic("cm_IoctlSysName: no cm_sysNameList entry to read\n"
!                                , __FILE__, __LINE__);
                  t = strlen((*sysnamelist)[count]);
                  if (t >= MAXSYSNAME)
!                     osi_panic("cm_IoctlSysName: sysname entry garbled\n"
!                                , __FILE__, __LINE__);
                  strcpy(cp, (*sysnamelist)[count]);
                  cp += t + 1;
              }
--- 1237,1248 ----
              cp += strlen(outname) + 1;	/* skip name and terminating null char */
              for ( count=1; count < foundname ; ++count) {   /* ... or list */
                  if ( !(*sysnamelist)[count] )
!                     osi_panic("cm_IoctlSysName: no cm_sysNameList entry to read\n", 
!                                __FILE__, __LINE__);
                  t = strlen((*sysnamelist)[count]);
                  if (t >= MAXSYSNAME)
!                     osi_panic("cm_IoctlSysName: sysname entry garbled\n", 
!                                __FILE__, __LINE__);
                  strcpy(cp, (*sysnamelist)[count]);
                  cp += t + 1;
              }
***************
*** 1223,1400 ****
          ioctlp->outDatap = cp;
      }
          
! 	/* done: success */
      return 0;
  }
  
  long cm_IoctlGetCellStatus(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	long temp;
!         cm_cell_t *cellp;
! 
! 	cm_SkipIoctlPath(ioctlp);
  
! 	cellp = cm_GetCell(ioctlp->inDatap, 0);
! 	if (!cellp) return CM_ERROR_NOSUCHCELL;
  
! 	temp = 0;
! 	lock_ObtainMutex(&cellp->mx);
!         if (cellp->flags & CM_CELLFLAG_SUID)
! 		temp |= CM_SETCELLFLAG_SUID;
! 	lock_ReleaseMutex(&cellp->mx);
          
!         /* now copy out parm */
!         memcpy(ioctlp->outDatap, &temp, sizeof(long));
!         ioctlp->outDatap += sizeof(long);
  
! 	return 0;
  }
  
  long cm_IoctlSetCellStatus(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	long temp;
!         cm_cell_t *cellp;
! 
! 	cm_SkipIoctlPath(ioctlp);
! 
! 	cellp = cm_GetCell(ioctlp->inDatap + 2*sizeof(long), 0);
! 	if (!cellp) return CM_ERROR_NOSUCHCELL;
  
! 	memcpy((char *)&temp, ioctlp->inDatap, sizeof(long));
  
! 	lock_ObtainMutex(&cellp->mx);
!         if (temp & CM_SETCELLFLAG_SUID)
!         	cellp->flags |= CM_CELLFLAG_SUID;
! 	else
!         	cellp->flags &= ~CM_CELLFLAG_SUID;
! 	lock_ReleaseMutex(&cellp->mx);
  
! 	return 0;
  }
  
  long cm_IoctlSetSPrefs(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	cm_SSetPref_t 	  *spin; /* input */
! 	cm_SPref_t        *srvin;   /* one input component */
! 	cm_server_t       *tsp;
! 	int 		  i, vlonly, noServers, type;
! 	struct sockaddr_in	tmp;
! 	unsigned short	  rank;
! 
! 	cm_SkipIoctlPath(ioctlp);       /* we don't care about the path */
! 
! 	spin	   = (cm_SSetPref_t *)ioctlp->inDatap;
! 	noServers  = spin->num_servers;
! 	vlonly     = spin->flags;
! 	if ( vlonly )
! 		type = CM_SERVER_VLDB;
! 	else    
          type = CM_SERVER_FILE;
  
! 	for ( i=0; i < noServers; i++) 
! 	{
! 		srvin          = &(spin->servers[i]);
! 		rank           = srvin->rank + (rand() & 0x000f);
! 		tmp.sin_addr   = srvin->host;
! 		tmp.sin_family = AF_INET;
! 
! 		tsp = cm_FindServer(&tmp, type);
! 		if ( tsp )		/* an existing server - ref count increased */
! 		{
! 			tsp->ipRank = rank; /* no need to protect by mutex*/
! 
! 			if ( type == CM_SERVER_FILE) /* fileserver */
! 			{
! 			    /* find volumes which might have RO copy 
! 			    /* on server and change the ordering of 
! 			    ** their RO list */
! 			    cm_ChangeRankVolume(tsp);
! 			}
! 			else 	
! 			{
! 			    /* set preferences for an existing vlserver */
! 			    cm_ChangeRankCellVLServer(tsp);
! 			}
              cm_PutServer(tsp);  /* decrease refcount */
! 		}
! 		else	/* add a new server without a cell */
! 		{
! 			tsp = cm_NewServer(&tmp, type, NULL); /* refcount = 1 */
! 			tsp->ipRank = rank;
! 		}
! 	}
! 	return 0;
  }
  
  long cm_IoctlGetSPrefs(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	cm_SPrefRequest_t *spin; /* input */
!    	cm_SPrefInfo_t    *spout;   /* output */
! 	cm_SPref_t        *srvout;   /* one output component */
! 	cm_server_t       *tsp;
! 	int 		  i, vlonly, noServers;
! 
! 	cm_SkipIoctlPath(ioctlp);       /* we don't care about the path */
! 
! 	spin      = (cm_SPrefRequest_t *)ioctlp->inDatap;
! 	spout     = (cm_SPrefInfo_t *) ioctlp->outDatap;
! 	srvout    = spout->servers;
! 	noServers = spin->num_servers; 
! 	vlonly    = spin->flags & CM_SPREF_VLONLY;
! 	spout->num_servers = 0;
! 
! 	lock_ObtainRead(&cm_serverLock); /* get server lock */
! 
! 	for(tsp=cm_allServersp, i=0; tsp && noServers; tsp=tsp->allNextp,i++){
! 		if (spin->offset > i) {
! 			continue;    /* catch up to where we left off */
! 		}
! 
! 		if ( vlonly && (tsp->type == CM_SERVER_FILE) )
! 			continue;   /* ignore fileserver for -vlserver option*/
! 		if ( !vlonly && (tsp->type == CM_SERVER_VLDB) )
! 			continue;   /* ignore vlservers */
! 
! 		srvout->host = tsp->addr.sin_addr;
! 		srvout->rank = tsp->ipRank;
! 		srvout++;	
! 		spout->num_servers++;
! 		noServers--;
! 	}
! 	lock_ReleaseRead(&cm_serverLock); /* release server lock */
! 
! 	if ( tsp ) 	/* we ran out of space in the output buffer */
! 		spout->next_offset = i;
! 	else    
! 		spout->next_offset = 0; 
! 	ioctlp->outDatap += sizeof(cm_SPrefInfo_t) + 
! 			(spout->num_servers -1 ) * sizeof(cm_SPref_t) ;
! 	return 0;
  }
  
  long cm_IoctlStoreBehind(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	/* we ignore default asynchrony since we only have one way
!          * of doing this today.
!          */
! 	return 0;
! }
  
  long cm_IoctlCreateMountPoint(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	char leaf[256];
      long code;
      cm_scache_t *dscp;
      cm_attr_t tattr;
      char *cp;
! 	cm_req_t req;
      char mpInfo[256];
      char fullCell[256];
! 	char volume[256];
! 	char cell[256];
! 	int ttl;
  
! 	cm_InitReq(&req);
          
      code = cm_ParseIoctlParent(ioctlp, userp, &req, &dscp, leaf);
      if (code) return code;
--- 1250,1429 ----
          ioctlp->outDatap = cp;
      }
          
!     /* done: success */
      return 0;
  }
  
  long cm_IoctlGetCellStatus(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     long temp;
!     cm_cell_t *cellp;
  
!     cm_SkipIoctlPath(ioctlp);
  
!     cellp = cm_GetCell(ioctlp->inDatap, 0);
!     if (!cellp) 
!         return CM_ERROR_NOSUCHCELL;
! 
!     temp = 0;
!     lock_ObtainMutex(&cellp->mx);
!     if (cellp->flags & CM_CELLFLAG_SUID)
!         temp |= CM_SETCELLFLAG_SUID;
!     lock_ReleaseMutex(&cellp->mx);
          
!     /* now copy out parm */
!     memcpy(ioctlp->outDatap, &temp, sizeof(long));
!     ioctlp->outDatap += sizeof(long);
  
!     return 0;
  }
  
  long cm_IoctlSetCellStatus(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     long temp;
!     cm_cell_t *cellp;
  
!     cm_SkipIoctlPath(ioctlp);
  
!     cellp = cm_GetCell(ioctlp->inDatap + 2*sizeof(long), 0);
!     if (!cellp) 
!         return CM_ERROR_NOSUCHCELL;
! 
!     memcpy((char *)&temp, ioctlp->inDatap, sizeof(long));
! 
!     lock_ObtainMutex(&cellp->mx);
!     if (temp & CM_SETCELLFLAG_SUID)
!         cellp->flags |= CM_CELLFLAG_SUID;
!     else
!         cellp->flags &= ~CM_CELLFLAG_SUID;
!     lock_ReleaseMutex(&cellp->mx);
  
!     return 0;
  }
  
  long cm_IoctlSetSPrefs(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     cm_SSetPref_t 	  *spin; /* input */
!     cm_SPref_t        *srvin;   /* one input component */
!     cm_server_t       *tsp;
!     int 		  i, vlonly, noServers, type;
!     struct sockaddr_in	tmp;
!     unsigned short	  rank;
! 
!     cm_SkipIoctlPath(ioctlp);       /* we don't care about the path */
! 
!     spin	   = (cm_SSetPref_t *)ioctlp->inDatap;
!     noServers  = spin->num_servers;
!     vlonly     = spin->flags;
!     if ( vlonly )
!         type = CM_SERVER_VLDB;
!     else    
          type = CM_SERVER_FILE;
  
!     for ( i=0; i < noServers; i++) 
!     {
!         srvin          = &(spin->servers[i]);
!         rank           = srvin->rank + (rand() & 0x000f);
!         tmp.sin_addr   = srvin->host;
!         tmp.sin_family = AF_INET;
! 
!         tsp = cm_FindServer(&tmp, type);
!         if ( tsp )		/* an existing server - ref count increased */
!         {
!             tsp->ipRank = rank; /* no need to protect by mutex*/
! 
!             if ( type == CM_SERVER_FILE) /* fileserver */
!             {
!                 /* find volumes which might have RO copy 
!                 /* on server and change the ordering of 
!                 ** their RO list */
!                     cm_ChangeRankVolume(tsp);
!             }
!             else 	
!             {
!                 /* set preferences for an existing vlserver */
!                 cm_ChangeRankCellVLServer(tsp);
!             }
              cm_PutServer(tsp);  /* decrease refcount */
!         }
!         else	/* add a new server without a cell */
!         {
!             tsp = cm_NewServer(&tmp, type, NULL); /* refcount = 1 */
!             tsp->ipRank = rank;
!         }
!     }
!     return 0;
  }
  
  long cm_IoctlGetSPrefs(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     cm_SPrefRequest_t *spin; /* input */
!     cm_SPrefInfo_t    *spout;   /* output */
!     cm_SPref_t        *srvout;   /* one output component */
!     cm_server_t       *tsp;
!     int 		  i, vlonly, noServers;
! 
!     cm_SkipIoctlPath(ioctlp);       /* we don't care about the path */
! 
!     spin      = (cm_SPrefRequest_t *)ioctlp->inDatap;
!     spout     = (cm_SPrefInfo_t *) ioctlp->outDatap;
!     srvout    = spout->servers;
!     noServers = spin->num_servers; 
!     vlonly    = spin->flags & CM_SPREF_VLONLY;
!     spout->num_servers = 0;
! 
!     lock_ObtainRead(&cm_serverLock); /* get server lock */
! 
!     for (tsp=cm_allServersp, i=0; tsp && noServers; tsp=tsp->allNextp,i++){
!         if (spin->offset > i) {
!             continue;    /* catch up to where we left off */
!         }
! 
!         if ( vlonly && (tsp->type == CM_SERVER_FILE) )
!             continue;   /* ignore fileserver for -vlserver option*/
!         if ( !vlonly && (tsp->type == CM_SERVER_VLDB) )
!             continue;   /* ignore vlservers */
! 
!         srvout->host = tsp->addr.sin_addr;
!         srvout->rank = tsp->ipRank;
!         srvout++;	
!         spout->num_servers++;
!         noServers--;
!     }
!     lock_ReleaseRead(&cm_serverLock); /* release server lock */
! 
!     if ( tsp ) 	/* we ran out of space in the output buffer */
!         spout->next_offset = i;
!     else    
!         spout->next_offset = 0; 
!     ioctlp->outDatap += sizeof(cm_SPrefInfo_t) + 
!         (spout->num_servers -1 ) * sizeof(cm_SPref_t) ;
!     return 0;
  }
  
  long cm_IoctlStoreBehind(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     /* we ignore default asynchrony since we only have one way
!      * of doing this today.
!      */
!     return 0;
! }       
  
  long cm_IoctlCreateMountPoint(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     char leaf[256];
      long code;
      cm_scache_t *dscp;
      cm_attr_t tattr;
      char *cp;
!     cm_req_t req;
      char mpInfo[256];
      char fullCell[256];
!     char volume[256];
!     char cell[256];
!     int ttl;
  
!     cm_InitReq(&req);
          
      code = cm_ParseIoctlParent(ioctlp, userp, &req, &dscp, leaf);
      if (code) return code;
***************
*** 1408,1456 ****
       * work on UNIX clients.
       */
  
! 	/* Extract the possibly partial cell name */
! 	strcpy(cell, ioctlp->inDatap + 1);      /* Skip the mp type character */
          
      if (cp = strchr(cell, ':')) {
! 		/* Extract the volume name */
          *cp = 0;
! 		strcpy(volume,  cp + 1);
  	
          /* Get the full name for this cell */
          code = cm_SearchCellFile(cell, fullCell, 0, 0);
  #ifdef AFS_AFSDB_ENV
! 		if (code && cm_dnsEnabled)
              code = cm_SearchCellByDNS(cell, fullCell, &ttl, 0, 0);
  #endif
          if (code)
! 			return CM_ERROR_NOSUCHCELL;
  	
          sprintf(mpInfo, "%c%s:%s", *ioctlp->inDatap, fullCell, volume);
! 	} else {
          /* No cell name specified */
          strcpy(mpInfo, ioctlp->inDatap);
      }
  
  #ifdef AFS_FREELANCE_CLIENT
! 	if (cm_freelanceEnabled && dscp == cm_rootSCachep) {
! 	  /* we are adding the mount point to the root dir., so call
! 	     the freelance code to do the add. */
! 	  osi_Log0(afsd_logp,"IoctlCreateMountPoint within Freelance root dir");
! 	  code = cm_FreelanceAddMount(leaf, fullCell, volume, 
!                                   *ioctlp->inDatap == '%', NULL);
! 	  return code;
! 	}
  #endif
! 	/* create the symlink with mode 644.  The lack of X bits tells
       * us that it is a mount point.
       */
! 	tattr.mask = CM_ATTRMASK_UNIXMODEBITS | CM_ATTRMASK_CLIENTMODTIME;
      tattr.unixModeBits = 0644;
! 	tattr.clientModTime = time(NULL);
  
      code = cm_SymLink(dscp, leaf, mpInfo, 0, &tattr, userp, &req);
! 	if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
! 		smb_NotifyChange(FILE_ACTION_ADDED,
                           FILE_NOTIFY_CHANGE_DIR_NAME,
                           dscp, leaf, NULL, TRUE);
  
--- 1437,1485 ----
       * work on UNIX clients.
       */
  
!     /* Extract the possibly partial cell name */
!     strcpy(cell, ioctlp->inDatap + 1);      /* Skip the mp type character */
          
      if (cp = strchr(cell, ':')) {
!         /* Extract the volume name */
          *cp = 0;
!         strcpy(volume,  cp + 1);
  	
          /* Get the full name for this cell */
          code = cm_SearchCellFile(cell, fullCell, 0, 0);
  #ifdef AFS_AFSDB_ENV
!         if (code && cm_dnsEnabled)
              code = cm_SearchCellByDNS(cell, fullCell, &ttl, 0, 0);
  #endif
          if (code)
!             return CM_ERROR_NOSUCHCELL;
  	
          sprintf(mpInfo, "%c%s:%s", *ioctlp->inDatap, fullCell, volume);
!     } else {
          /* No cell name specified */
          strcpy(mpInfo, ioctlp->inDatap);
      }
  
  #ifdef AFS_FREELANCE_CLIENT
!     if (cm_freelanceEnabled && dscp == cm_rootSCachep) {
!         /* we are adding the mount point to the root dir., so call
!          * the freelance code to do the add. */
!         osi_Log0(afsd_logp,"IoctlCreateMountPoint within Freelance root dir");
!         code = cm_FreelanceAddMount(leaf, fullCell, volume, 
!                                     *ioctlp->inDatap == '%', NULL);
!         return code;
!     }
  #endif
!     /* create the symlink with mode 644.  The lack of X bits tells
       * us that it is a mount point.
       */
!     tattr.mask = CM_ATTRMASK_UNIXMODEBITS | CM_ATTRMASK_CLIENTMODTIME;
      tattr.unixModeBits = 0644;
!     tattr.clientModTime = time(NULL);
  
      code = cm_SymLink(dscp, leaf, mpInfo, 0, &tattr, userp, &req);
!     if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
!         smb_NotifyChange(FILE_ACTION_ADDED,
                           FILE_NOTIFY_CHANGE_DIR_NAME,
                           dscp, leaf, NULL, TRUE);
  
***************
*** 1460,2022 ****
  
  long cm_IoctlSymlink(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	char leaf[256];
! 	long code;
! 	cm_scache_t *dscp;
! 	cm_attr_t tattr;
! 	char *cp;
! 	cm_req_t req;
! 
! 	cm_InitReq(&req);
  
! 	code = cm_ParseIoctlParent(ioctlp, userp, &req, &dscp, leaf);
! 	if (code) return code;
  
!         /* Translate chars for the link name */
!         TranslateExtendedChars(leaf);
  
!         /* Translate chars for the linked to name */
!         TranslateExtendedChars(ioctlp->inDatap);
  
! 	cp = ioctlp->inDatap;		/* contents of link */
  
! 	/* Create symlink with mode 0755. */
! 	tattr.mask = CM_ATTRMASK_UNIXMODEBITS;
! 	tattr.unixModeBits = 0755;
  
! 	code = cm_SymLink(dscp, leaf, cp, 0, &tattr, userp, &req);
! 	if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
! 		smb_NotifyChange(FILE_ACTION_ADDED,
! 				 FILE_NOTIFY_CHANGE_FILE_NAME
! 				   | FILE_NOTIFY_CHANGE_DIR_NAME,
! 				 dscp, leaf, NULL, TRUE);
  
! 	cm_ReleaseSCache(dscp);
  
! 	return code;
  }
  
  extern long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
! 	cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
! 	cm_user_t *userp, cm_req_t *reqp);
  
  long cm_IoctlListlink(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	long code;
! 	cm_scache_t *dscp;
! 	cm_scache_t *scp;
! 	char *cp;
! 	cm_space_t *spacep;
! 	cm_scache_t *newRootScp;
! 	cm_req_t req;
! 
! 	cm_InitReq(&req);
! 
! 	code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp);
! 	if (code) return code;
! 
! 	cp = ioctlp->inDatap;
! 
! 	code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
! 	cm_ReleaseSCache(dscp);
! 	if (code) return code;
! 
! 	/* Check that it's a real symlink */
! 	if (scp->fileType != CM_SCACHETYPE_SYMLINK){
! 		cm_ReleaseSCache(scp);
! 		return CM_ERROR_INVAL;
! 	}
! 
! 	code = cm_AssembleLink(scp, "", &newRootScp, &spacep, userp, &req);
! 	cm_ReleaseSCache(scp);
! 	if (code == 0) {
! 		cp = ioctlp->outDatap;
! 		if (newRootScp != NULL) {
              strcpy(cp, cm_mountRoot);
              strcat(cp, "/");
! 			cp += strlen(cp);
! 		}
! 		strcpy(cp, spacep->data);
! 		cp += strlen(cp) + 1;
! 		ioctlp->outDatap = cp;
! 		cm_FreeSpace(spacep);
! 		if (newRootScp != NULL)
! 			cm_ReleaseSCache(newRootScp);
! 	}
  
! 	return code;
  }
  
  long cm_IoctlIslink(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {/*CHECK FOR VALID SYMLINK*/
! 	long code;
! 	cm_scache_t *dscp;
! 	cm_scache_t *scp;
! 	char *cp;
! 	cm_req_t req;
! 
! 	cm_InitReq(&req);
! 
! 	code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp);
! 	if (code) return code;
! 
! 	cp = ioctlp->inDatap;
! 	osi_LogEvent("cm_IoctlListlink",NULL," name[%s]",cp);
! 
! 	code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
! 	cm_ReleaseSCache(dscp);
! 	if (code) return code;
! 
! 	/* Check that it's a real symlink */
! 	if (scp->fileType != CM_SCACHETYPE_SYMLINK)
! 		code = CM_ERROR_INVAL;
! 	cm_ReleaseSCache(scp);
! 	return code;
  }
  
  long cm_IoctlDeletelink(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	long code;
!         cm_scache_t *dscp;
!         cm_scache_t *scp;
! 	char *cp;
!         cm_req_t req;
  
! 	cm_InitReq(&req);
  
!         code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp);
!         if (code) return code;
  
!         cp = ioctlp->inDatap;
  
! 	code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
          
! 	/* if something went wrong, bail out now */
!         if (code) {
! 		goto done;
! 	}
          
! 	lock_ObtainMutex(&scp->mx);
!         code = cm_SyncOp(scp, NULL, userp, &req, 0,
!         	CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
!         if (code) {
!         	lock_ReleaseMutex(&scp->mx);
!         	cm_ReleaseSCache(scp);
! 		goto done;
! 	}
! 	
!         /* now check that this is a real symlink */
!         if (scp->fileType != CM_SCACHETYPE_SYMLINK) {
! 		lock_ReleaseMutex(&scp->mx);
!         	cm_ReleaseSCache(scp);
!                 code = CM_ERROR_INVAL;
!                 goto done;
!         }
  	
!         /* time to make the RPC, so drop the lock */
! 	lock_ReleaseMutex(&scp->mx);
          cm_ReleaseSCache(scp);
          
! 	/* easier to do it this way */
!         code = cm_Unlink(dscp, cp, userp, &req);
! 	if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
! 		smb_NotifyChange(FILE_ACTION_REMOVED,
! 				 FILE_NOTIFY_CHANGE_FILE_NAME
! 				   | FILE_NOTIFY_CHANGE_DIR_NAME,
! 				 dscp, cp, NULL, TRUE);
! 
! done:
! 	cm_ReleaseSCache(dscp);
! 	return code;
  }
  
  long cm_IoctlSetToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	char *saveDataPtr;
! 	char *tp;
! 	int ticketLen;
! 	char *ticket;
! 	int ctSize;
! 	struct ClearToken ct;
! 	cm_cell_t *cellp;
! 	cm_ucell_t *ucellp;
! 	char *uname = NULL;
! 	afs_uuid_t uuid;
! 	int flags;
! 	char sessionKey[8];
! 	char *smbname;
! 
! 	saveDataPtr = ioctlp->inDatap;
! 
! 	cm_SkipIoctlPath(ioctlp);
! 
! 	tp = ioctlp->inDatap;
! 
! 	/* ticket length */
! 	memcpy(&ticketLen, tp, sizeof(ticketLen));
! 	tp += sizeof(ticketLen);
! 	if (ticketLen < MINKTCTICKETLEN || ticketLen > MAXKTCTICKETLEN)
! 		return CM_ERROR_INVAL;
! 
! 	/* remember ticket and skip over it for now */
! 	ticket = tp;
! 	tp += ticketLen;
! 
! 	/* clear token size */
! 	memcpy(&ctSize, tp, sizeof(ctSize));
! 	tp += sizeof(ctSize);
! 	if (ctSize != sizeof(struct ClearToken))
! 		return CM_ERROR_INVAL;
! 
! 	/* clear token */
! 	memcpy(&ct, tp, ctSize);
! 	tp += ctSize;
! 	if (ct.AuthHandle == -1)
! 		ct.AuthHandle = 999;	/* more rxvab compat stuff */
! 
! 	/* more stuff, if any */
! 	if (ioctlp->inCopied > tp - saveDataPtr) {
! 		/* flags:  logon flag */
! 		memcpy(&flags, tp, sizeof(int));
! 		tp += sizeof(int);
! 
! 		/* cell name */
! 		cellp = cm_GetCell(tp, CM_FLAG_CREATE);
! 		if (!cellp) return CM_ERROR_NOSUCHCELL;
! 		tp += strlen(tp) + 1;
! 
! 		/* user name */
! 		uname = tp;
! 		tp += strlen(tp) + 1;
  
          if (flags & PIOCTL_LOGON) {
! 		  /* SMB user name with which to associate tokens */
! 		  smbname = tp;
!           osi_Log2(smb_logp,"cm_IoctlSetToken for user [%s] smbname [%s]",
!                     osi_LogSaveString(smb_logp,uname), osi_LogSaveString(smb_logp,smbname));
!           fprintf(stderr, "SMB name = %s\n", smbname);
! 		  tp += strlen(tp) + 1;
          } else {
              osi_Log1(smb_logp,"cm_IoctlSetToken for user [%s]",
!                       osi_LogSaveString(smb_logp,uname));
          }
  
  #ifndef DJGPP   /* for win95, session key is back in pioctl */
  		/* uuid */
! 		memcpy(&uuid, tp, sizeof(uuid));
! 		if (!cm_FindTokenEvent(uuid, sessionKey))
! 			return CM_ERROR_INVAL;
  #endif /* !DJGPP */
! 	} else {
! 		cellp = cm_rootCellp;
          osi_Log0(smb_logp,"cm_IoctlSetToken - no name specified");
      }
  
! 	if (flags & PIOCTL_LOGON) {
!           userp = smb_FindCMUserByName(smbname, ioctlp->fidp->vcp->rname);
! 	}
! 	
! 	/* store the token */
! 	lock_ObtainMutex(&userp->mx);
! 	ucellp = cm_GetUCell(userp, cellp);
      osi_Log1(smb_logp,"cm_IoctlSetToken ucellp %lx", ucellp);
! 	ucellp->ticketLen = ticketLen;
! 	if (ucellp->ticketp)
! 		free(ucellp->ticketp);	/* Discard old token if any */
! 	ucellp->ticketp = malloc(ticketLen);
! 	memcpy(ucellp->ticketp, ticket, ticketLen);
  #ifndef DJGPP
! 	/*
! 	 * Get the session key from the RPC, rather than from the pioctl.
! 	 */
! 	/*
! 	memcpy(&ucellp->sessionKey, ct.HandShakeKey, sizeof(ct.HandShakeKey));
! 	 */
! 	memcpy(ucellp->sessionKey.data, sessionKey, sizeof(sessionKey));
  #else
!         /* for win95, we are getting the session key from the pioctl */
!         memcpy(&ucellp->sessionKey, ct.HandShakeKey, sizeof(ct.HandShakeKey));
  #endif /* !DJGPP */
! 	ucellp->kvno = ct.AuthHandle;
! 	ucellp->expirationTime = ct.EndTimestamp;
! 	ucellp->gen++;
! 	if (uname) strcpy(ucellp->userName, uname);
! 	ucellp->flags |= CM_UCELLFLAG_RXKAD;
! 	lock_ReleaseMutex(&userp->mx);
! 
! 	if (flags & PIOCTL_LOGON) {
! 		ioctlp->flags |= SMB_IOCTLFLAG_LOGON;
! 	}
  
! 	cm_ResetACLCache(userp);
  
! 	return 0;
  }
  
  long cm_IoctlGetTokenIter(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	char *tp, *cp;
! 	int iterator;
! 	int temp;
! 	cm_ucell_t *ucellp;
! 	struct ClearToken ct;
! 
! 	cm_SkipIoctlPath(ioctlp);
! 
! 	tp = ioctlp->inDatap;
! 	cp = ioctlp->outDatap;
! 
! 	/* iterator */
! 	memcpy(&iterator, tp, sizeof(iterator));
! 	tp += sizeof(iterator);
! 
! 	lock_ObtainMutex(&userp->mx);
! 
! 	/* look for token */
! 	for (;;iterator++) {
! 		ucellp = cm_FindUCell(userp, iterator);
! 		if (!ucellp) {
! 			lock_ReleaseMutex(&userp->mx);
! 			return CM_ERROR_NOMORETOKENS;
! 		}
! 		if (ucellp->flags & CM_UCELLFLAG_RXKAD)
! 			break;
! 	}
! 
! 	/* new iterator */
! 	temp = ucellp->iterator + 1;
! 	memcpy(cp, &temp, sizeof(temp));
! 	cp += sizeof(temp);
! 
! 	/* ticket length */
! 	memcpy(cp, &ucellp->ticketLen, sizeof(ucellp->ticketLen));
! 	cp += sizeof(ucellp->ticketLen);
! 
! 	/* ticket */
! 	memcpy(cp, ucellp->ticketp, ucellp->ticketLen);
! 	cp += ucellp->ticketLen;
! 
! 	/* clear token size */
! 	temp = sizeof(ct);
! 	memcpy(cp, &temp, sizeof(temp));
! 	cp += sizeof(temp);
  
! 	/* clear token */
! 	ct.AuthHandle = ucellp->kvno;
  #ifndef DJGPP
! 	/*
! 	 * Don't give out a real session key here
! 	 */
! 	/*
! 	memcpy(ct.HandShakeKey, &ucellp->sessionKey, sizeof(ct.HandShakeKey));
! 	 */
! 	memset(ct.HandShakeKey, 0, sizeof(ct.HandShakeKey));
  #else
! 	memcpy(ct.HandShakeKey, &ucellp->sessionKey, sizeof(ct.HandShakeKey));
  #endif /* !DJGPP */
! 	ct.ViceId = 37;			/* XXX */
! 	ct.BeginTimestamp = 0;		/* XXX */
! 	ct.EndTimestamp = ucellp->expirationTime;
! 	memcpy(cp, &ct, sizeof(ct));
! 	cp += sizeof(ct);
! 
! 	/* Primary flag (unused) */
! 	temp = 0;
! 	memcpy(cp, &temp, sizeof(temp));
! 	cp += sizeof(temp);
! 
! 	/* cell name */
! 	strcpy(cp, ucellp->cellp->namep);
! 	cp += strlen(cp) + 1;
! 
! 	/* user name */
! 	strcpy(cp, ucellp->userName);
! 	cp += strlen(cp) + 1;
  
! 	ioctlp->outDatap = cp;
  
! 	lock_ReleaseMutex(&userp->mx);
  
! 	return 0;
  }
  
  long cm_IoctlGetToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	char *cp;
! 	int temp;
! 	cm_cell_t *cellp;
! 	cm_ucell_t *ucellp;
! 	struct ClearToken ct;
! 	char *tp;
  #ifndef DJGPP
! 	afs_uuid_t uuid;
  #endif /* !DJGPP */
  
! 	cm_SkipIoctlPath(ioctlp);
  
! 	tp = ioctlp->inDatap;
  
! 	cp = ioctlp->outDatap;
  
! 	/* cell name is right here */
! 	cellp = cm_GetCell(tp, 0);
! 	if (!cellp) return CM_ERROR_NOSUCHCELL;
! 	tp += strlen(tp) + 1;
  
  #ifndef DJGPP
! 	/* uuid */
! 	memcpy(&uuid, tp, sizeof(uuid));
  #endif /* !DJGPP */
  
! 	lock_ObtainMutex(&userp->mx);
  
! 	ucellp = cm_GetUCell(userp, cellp);
! 	if (!ucellp || !(ucellp->flags & CM_UCELLFLAG_RXKAD)) {
! 		lock_ReleaseMutex(&userp->mx);
! 		return CM_ERROR_NOMORETOKENS;
! 	}
! 
! 	/* ticket length */
! 	memcpy(cp, &ucellp->ticketLen, sizeof(ucellp->ticketLen));
! 	cp += sizeof(ucellp->ticketLen);
! 
! 	/* ticket */
! 	memcpy(cp, ucellp->ticketp, ucellp->ticketLen);
! 	cp += ucellp->ticketLen;
! 
! 	/* clear token size */
! 	temp = sizeof(ct);
! 	memcpy(cp, &temp, sizeof(temp));
! 	cp += sizeof(temp);
  
! 	/* clear token */
! 	ct.AuthHandle = ucellp->kvno;
  #ifndef DJGPP
! 	/*
! 	 * Don't give out a real session key here
! 	 */
! 	/*
! 	memcpy(ct.HandShakeKey, &ucellp->sessionKey, sizeof(ct.HandShakeKey));
! 	 */
! 	memset(ct.HandShakeKey, 0, sizeof(ct.HandShakeKey));
  #else
!         memcpy(ct.HandShakeKey, &ucellp->sessionKey, sizeof(ct.HandShakeKey));
  #endif /* !DJGPP */
! 	ct.ViceId = 37;			/* XXX */
! 	ct.BeginTimestamp = 0;		/* XXX */
! 	ct.EndTimestamp = ucellp->expirationTime;
! 	memcpy(cp, &ct, sizeof(ct));
! 	cp += sizeof(ct);
! 
! 	/* Primary flag (unused) */
! 	temp = 0;
! 	memcpy(cp, &temp, sizeof(temp));
! 	cp += sizeof(temp);
! 
! 	/* cell name */
! 	strcpy(cp, ucellp->cellp->namep);
! 	cp += strlen(cp) + 1;
! 
! 	/* user name */
! 	strcpy(cp, ucellp->userName);
! 	cp += strlen(cp) + 1;
  
! 	ioctlp->outDatap = cp;
  
! 	lock_ReleaseMutex(&userp->mx);
  
  #ifndef DJGPP
! 	cm_RegisterNewTokenEvent(uuid, ucellp->sessionKey.data);
  #endif /* !DJGPP */
  
! 	return 0;
  }
  
  long cm_IoctlDelToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	char *cp;
! 	cm_cell_t *cellp;
! 	cm_ucell_t *ucellp;
! 
! 	cm_SkipIoctlPath(ioctlp);
! 
! 	cp = ioctlp->outDatap;
  
! 	/* cell name is right here */
! 	cellp = cm_GetCell(ioctlp->inDatap, 0);
! 	if (!cellp) return CM_ERROR_NOSUCHCELL;
  
! 	lock_ObtainMutex(&userp->mx);
  
! 	ucellp = cm_GetUCell(userp, cellp);
! 	if (!ucellp) {
! 		lock_ReleaseMutex(&userp->mx);
! 		return CM_ERROR_NOMORETOKENS;
! 	}
  
      osi_Log1(smb_logp,"cm_IoctlDelToken ucellp %lx", ucellp);
  
! 	if (ucellp->ticketp) {
! 		free(ucellp->ticketp);
! 		ucellp->ticketp = NULL;
! 	}
! 	ucellp->flags &= ~CM_UCELLFLAG_RXKAD;
! 	ucellp->gen++;
  
! 	lock_ReleaseMutex(&userp->mx);
  
! 	cm_ResetACLCache(userp);
  
! 	return 0;
  }
  
  long cm_IoctlDelAllToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
! 	cm_ucell_t *ucellp;
  
! 	lock_ObtainMutex(&userp->mx);
  
      for (ucellp = userp->cellInfop; ucellp; ucellp = ucellp->nextp) {
          osi_Log1(smb_logp,"cm_IoctlDelAllToken ucellp %lx", ucellp);
! 		ucellp->flags &= ~CM_UCELLFLAG_RXKAD;
! 		ucellp->gen++;
! 	}
  
! 	lock_ReleaseMutex(&userp->mx);
  
! 	cm_ResetACLCache(userp);
  
! 	return 0;
  }
  
  long cm_IoctlMakeSubmount(smb_ioctl_t *ioctlp, cm_user_t *userp)
  {
! 	char afspath[MAX_PATH];
! 	char *submountreqp;
! 	int nextAutoSubmount;
      HKEY hkSubmounts;
      DWORD dwType, dwSize;
      DWORD status;
      DWORD dwIndex;
      DWORD dwSubmounts;
  
! 	cm_SkipIoctlPath(ioctlp);
  
! 	/* Serialize this one, to prevent simultaneous mods
! 	 * to afsdsbmt.ini
! 	 */
! 	lock_ObtainMutex(&cm_Afsdsbmt_Lock);
! 
! 	/* Parse the input parameters--first the required afs path,
! 	 * then the requested submount name (which may be "").
! 	 */
! 	cm_NormalizeAfsPath (afspath, ioctlp->inDatap);
! 	submountreqp = ioctlp->inDatap + (strlen(ioctlp->inDatap)+1);
! 
! 	/* If the caller supplied a suggested submount name, see if
! 	 * that submount name is in use... if so, the submount's path
! 	 * has to match our path.
! 	 */
  
      RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
                      "SOFTWARE\\OpenAFS\\Client\\Submounts",
--- 1489,2051 ----
  
  long cm_IoctlSymlink(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     char leaf[256];
!     long code;
!     cm_scache_t *dscp;
!     cm_attr_t tattr;
!     char *cp;
!     cm_req_t req;
  
!     cm_InitReq(&req);
  
!     code = cm_ParseIoctlParent(ioctlp, userp, &req, &dscp, leaf);
!     if (code) return code;
  
!     /* Translate chars for the link name */
!     TranslateExtendedChars(leaf);
  
!     /* Translate chars for the linked to name */
!     TranslateExtendedChars(ioctlp->inDatap);
  
!     cp = ioctlp->inDatap;		/* contents of link */
  
!     /* Create symlink with mode 0755. */
!     tattr.mask = CM_ATTRMASK_UNIXMODEBITS;
!     tattr.unixModeBits = 0755;
! 
!     code = cm_SymLink(dscp, leaf, cp, 0, &tattr, userp, &req);
!     if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
!         smb_NotifyChange(FILE_ACTION_ADDED,
!                           FILE_NOTIFY_CHANGE_FILE_NAME
!                           | FILE_NOTIFY_CHANGE_DIR_NAME,
!                           dscp, leaf, NULL, TRUE);
  
!     cm_ReleaseSCache(dscp);
  
!     return code;
  }
  
  extern long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
!                             cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
!                             cm_user_t *userp, cm_req_t *reqp);
  
  long cm_IoctlListlink(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     long code;
!     cm_scache_t *dscp;
!     cm_scache_t *scp;
!     char *cp;
!     cm_space_t *spacep;
!     cm_scache_t *newRootScp;
!     cm_req_t req;
! 
!     cm_InitReq(&req);
! 
!     code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp);
!     if (code) return code;
! 
!     cp = ioctlp->inDatap;
! 
!     code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
!     cm_ReleaseSCache(dscp);
!     if (code) return code;
! 
!     /* Check that it's a real symlink */
!     if (scp->fileType != CM_SCACHETYPE_SYMLINK){
!         cm_ReleaseSCache(scp);
!         return CM_ERROR_INVAL;
!     }
! 
!     code = cm_AssembleLink(scp, "", &newRootScp, &spacep, userp, &req);
!     cm_ReleaseSCache(scp);
!     if (code == 0) {
!         cp = ioctlp->outDatap;
!         if (newRootScp != NULL) {
              strcpy(cp, cm_mountRoot);
              strcat(cp, "/");
!             cp += strlen(cp);
!         }
!         strcpy(cp, spacep->data);
!         cp += strlen(cp) + 1;
!         ioctlp->outDatap = cp;
!         cm_FreeSpace(spacep);
!         if (newRootScp != NULL)
!             cm_ReleaseSCache(newRootScp);
!     }       
  
!     return code;
  }
  
  long cm_IoctlIslink(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {/*CHECK FOR VALID SYMLINK*/
!     long code;
!     cm_scache_t *dscp;
!     cm_scache_t *scp;
!     char *cp;
!     cm_req_t req;
! 
!     cm_InitReq(&req);
! 
!     code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp);
!     if (code) return code;
! 
!     cp = ioctlp->inDatap;
!     osi_LogEvent("cm_IoctlListlink",NULL," name[%s]",cp);
! 
!     code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
!     cm_ReleaseSCache(dscp);
!     if (code) return code;
! 
!     /* Check that it's a real symlink */
!     if (scp->fileType != CM_SCACHETYPE_SYMLINK)
!         code = CM_ERROR_INVAL;
!     cm_ReleaseSCache(scp);
!     return code;
  }
  
  long cm_IoctlDeletelink(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     long code;
!     cm_scache_t *dscp;
!     cm_scache_t *scp;
!     char *cp;
!     cm_req_t req;
  
!     cm_InitReq(&req);
  
!     code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp);
!     if (code) return code;
  
!     cp = ioctlp->inDatap;
  
!     code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
          
!     /* if something went wrong, bail out now */
!     if (code) {
!         goto done;
!     }
          
!     lock_ObtainMutex(&scp->mx);
!     code = cm_SyncOp(scp, NULL, userp, &req, 0,
!                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
!     if (code) {     
!         lock_ReleaseMutex(&scp->mx);
!         cm_ReleaseSCache(scp);
!         goto done;
!     }
  	
!     /* now check that this is a real symlink */
!     if (scp->fileType != CM_SCACHETYPE_SYMLINK) {
!         lock_ReleaseMutex(&scp->mx);
          cm_ReleaseSCache(scp);
+         code = CM_ERROR_INVAL;
+         goto done;
+     }
+ 	
+     /* time to make the RPC, so drop the lock */
+     lock_ReleaseMutex(&scp->mx);
+     cm_ReleaseSCache(scp);
          
!     /* easier to do it this way */
!     code = cm_Unlink(dscp, cp, userp, &req);
!     if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
!         smb_NotifyChange(FILE_ACTION_REMOVED,
!                           FILE_NOTIFY_CHANGE_FILE_NAME
!                           | FILE_NOTIFY_CHANGE_DIR_NAME,
!                           dscp, cp, NULL, TRUE);
! 
!   done:
!     cm_ReleaseSCache(dscp);
!     return code;
  }
  
  long cm_IoctlSetToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     char *saveDataPtr;
!     char *tp;
!     int ticketLen;
!     char *ticket;
!     int ctSize;
!     struct ClearToken ct;
!     cm_cell_t *cellp;
!     cm_ucell_t *ucellp;
!     char *uname = NULL;
!     afs_uuid_t uuid;
!     int flags;
!     char sessionKey[8];
!     char *smbname;
! 
!     saveDataPtr = ioctlp->inDatap;
! 
!     cm_SkipIoctlPath(ioctlp);
! 
!     tp = ioctlp->inDatap;
! 
!     /* ticket length */
!     memcpy(&ticketLen, tp, sizeof(ticketLen));
!     tp += sizeof(ticketLen);
!     if (ticketLen < MINKTCTICKETLEN || ticketLen > MAXKTCTICKETLEN)
!         return CM_ERROR_INVAL;
! 
!     /* remember ticket and skip over it for now */
!     ticket = tp;
!     tp += ticketLen;
! 
!     /* clear token size */
!     memcpy(&ctSize, tp, sizeof(ctSize));
!     tp += sizeof(ctSize);
!     if (ctSize != sizeof(struct ClearToken))
!         return CM_ERROR_INVAL;
! 
!     /* clear token */
!     memcpy(&ct, tp, ctSize);
!     tp += ctSize;
!     if (ct.AuthHandle == -1)
!         ct.AuthHandle = 999;	/* more rxvab compat stuff */
! 
!     /* more stuff, if any */
!     if (ioctlp->inCopied > tp - saveDataPtr) {
!         /* flags:  logon flag */
!         memcpy(&flags, tp, sizeof(int));
!         tp += sizeof(int);
! 
!         /* cell name */
!         cellp = cm_GetCell(tp, CM_FLAG_CREATE);
!         if (!cellp) return CM_ERROR_NOSUCHCELL;
!         tp += strlen(tp) + 1;
! 
!         /* user name */
!         uname = tp;
!         tp += strlen(tp) + 1;
  
          if (flags & PIOCTL_LOGON) {
!             /* SMB user name with which to associate tokens */
!             smbname = tp;
!             osi_Log2(smb_logp,"cm_IoctlSetToken for user [%s] smbname [%s]",
!                      osi_LogSaveString(smb_logp,uname), osi_LogSaveString(smb_logp,smbname));
!             fprintf(stderr, "SMB name = %s\n", smbname);
!             tp += strlen(tp) + 1;
          } else {
              osi_Log1(smb_logp,"cm_IoctlSetToken for user [%s]",
!                      osi_LogSaveString(smb_logp,uname));
          }
  
  #ifndef DJGPP   /* for win95, session key is back in pioctl */
  		/* uuid */
!         memcpy(&uuid, tp, sizeof(uuid));
!         if (!cm_FindTokenEvent(uuid, sessionKey))
!             return CM_ERROR_INVAL;
  #endif /* !DJGPP */
!     } else {
!         cellp = cm_rootCellp;
          osi_Log0(smb_logp,"cm_IoctlSetToken - no name specified");
      }
  
!     if (flags & PIOCTL_LOGON) {
!         userp = smb_FindCMUserByName(smbname, ioctlp->fidp->vcp->rname);
!     }
! 
!     /* store the token */
!     lock_ObtainMutex(&userp->mx);
!     ucellp = cm_GetUCell(userp, cellp);
      osi_Log1(smb_logp,"cm_IoctlSetToken ucellp %lx", ucellp);
!     ucellp->ticketLen = ticketLen;
!     if (ucellp->ticketp)
!         free(ucellp->ticketp);	/* Discard old token if any */
!     ucellp->ticketp = malloc(ticketLen);
!     memcpy(ucellp->ticketp, ticket, ticketLen);
  #ifndef DJGPP
!     /*
!      * Get the session key from the RPC, rather than from the pioctl.
!      */
!     /*
!     memcpy(&ucellp->sessionKey, ct.HandShakeKey, sizeof(ct.HandShakeKey));
!     */
!     memcpy(ucellp->sessionKey.data, sessionKey, sizeof(sessionKey));
  #else
!     /* for win95, we are getting the session key from the pioctl */
!     memcpy(&ucellp->sessionKey, ct.HandShakeKey, sizeof(ct.HandShakeKey));
  #endif /* !DJGPP */
!     ucellp->kvno = ct.AuthHandle;
!     ucellp->expirationTime = ct.EndTimestamp;
!     ucellp->gen++;
!     if (uname) strcpy(ucellp->userName, uname);
!     ucellp->flags |= CM_UCELLFLAG_RXKAD;
!     lock_ReleaseMutex(&userp->mx);
! 
!     if (flags & PIOCTL_LOGON) {
!         ioctlp->flags |= SMB_IOCTLFLAG_LOGON;
!     }
  
!     cm_ResetACLCache(userp);
  
!     return 0;
  }
  
  long cm_IoctlGetTokenIter(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     char *tp, *cp;
!     int iterator;
!     int temp;
!     cm_ucell_t *ucellp;
!     struct ClearToken ct;
! 
!     cm_SkipIoctlPath(ioctlp);
! 
!     tp = ioctlp->inDatap;
!     cp = ioctlp->outDatap;
! 
!     /* iterator */
!     memcpy(&iterator, tp, sizeof(iterator));
!     tp += sizeof(iterator);
! 
!     lock_ObtainMutex(&userp->mx);
! 
!     /* look for token */
!     for (;;iterator++) {
!         ucellp = cm_FindUCell(userp, iterator);
!         if (!ucellp) {
!             lock_ReleaseMutex(&userp->mx);
!             return CM_ERROR_NOMORETOKENS;
!         }
!         if (ucellp->flags & CM_UCELLFLAG_RXKAD)
!             break;
!     }       
! 
!     /* new iterator */
!     temp = ucellp->iterator + 1;
!     memcpy(cp, &temp, sizeof(temp));
!     cp += sizeof(temp);
! 
!     /* ticket length */
!     memcpy(cp, &ucellp->ticketLen, sizeof(ucellp->ticketLen));
!     cp += sizeof(ucellp->ticketLen);
! 
!     /* ticket */
!     memcpy(cp, ucellp->ticketp, ucellp->ticketLen);
!     cp += ucellp->ticketLen;
! 
!     /* clear token size */
!     temp = sizeof(ct);
!     memcpy(cp, &temp, sizeof(temp));
!     cp += sizeof(temp);
  
!     /* clear token */
!     ct.AuthHandle = ucellp->kvno;
  #ifndef DJGPP
!     /*
!      * Don't give out a real session key here
!      */
!     /*
!     memcpy(ct.HandShakeKey, &ucellp->sessionKey, sizeof(ct.HandShakeKey));
!     */
!     memset(ct.HandShakeKey, 0, sizeof(ct.HandShakeKey));
  #else
!     memcpy(ct.HandShakeKey, &ucellp->sessionKey, sizeof(ct.HandShakeKey));
  #endif /* !DJGPP */
!     ct.ViceId = 37;			/* XXX */
!     ct.BeginTimestamp = 0;		/* XXX */
!     ct.EndTimestamp = ucellp->expirationTime;
!     memcpy(cp, &ct, sizeof(ct));
!     cp += sizeof(ct);
! 
!     /* Primary flag (unused) */
!     temp = 0;
!     memcpy(cp, &temp, sizeof(temp));
!     cp += sizeof(temp);
! 
!     /* cell name */
!     strcpy(cp, ucellp->cellp->namep);
!     cp += strlen(cp) + 1;
! 
!     /* user name */
!     strcpy(cp, ucellp->userName);
!     cp += strlen(cp) + 1;
  
!     ioctlp->outDatap = cp;
  
!     lock_ReleaseMutex(&userp->mx);
  
!     return 0;
  }
  
  long cm_IoctlGetToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     char *cp;
!     int temp;
!     cm_cell_t *cellp;
!     cm_ucell_t *ucellp;
!     struct ClearToken ct;
!     char *tp;
  #ifndef DJGPP
!     afs_uuid_t uuid;
  #endif /* !DJGPP */
  
!     cm_SkipIoctlPath(ioctlp);
  
!     tp = ioctlp->inDatap;
  
!     cp = ioctlp->outDatap;
  
!     /* cell name is right here */
!     cellp = cm_GetCell(tp, 0);
!     if (!cellp) return CM_ERROR_NOSUCHCELL;
!     tp += strlen(tp) + 1;
  
  #ifndef DJGPP
!     /* uuid */
!     memcpy(&uuid, tp, sizeof(uuid));
  #endif /* !DJGPP */
  
!     lock_ObtainMutex(&userp->mx);
! 
!     ucellp = cm_GetUCell(userp, cellp);
!     if (!ucellp || !(ucellp->flags & CM_UCELLFLAG_RXKAD)) {
!         lock_ReleaseMutex(&userp->mx);
!         return CM_ERROR_NOMORETOKENS;
!     }
  
!     /* ticket length */
!     memcpy(cp, &ucellp->ticketLen, sizeof(ucellp->ticketLen));
!     cp += sizeof(ucellp->ticketLen);
! 
!     /* ticket */
!     memcpy(cp, ucellp->ticketp, ucellp->ticketLen);
!     cp += ucellp->ticketLen;
! 
!     /* clear token size */
!     temp = sizeof(ct);
!     memcpy(cp, &temp, sizeof(temp));
!     cp += sizeof(temp);
  
!     /* clear token */
!     ct.AuthHandle = ucellp->kvno;
  #ifndef DJGPP
!     /*
!      * Don't give out a real session key here
!      */
!     /*
!     memcpy(ct.HandShakeKey, &ucellp->sessionKey, sizeof(ct.HandShakeKey));
!     */
!     memset(ct.HandShakeKey, 0, sizeof(ct.HandShakeKey));
  #else
!     memcpy(ct.HandShakeKey, &ucellp->sessionKey, sizeof(ct.HandShakeKey));
  #endif /* !DJGPP */
!     ct.ViceId = 37;			/* XXX */
!     ct.BeginTimestamp = 0;		/* XXX */
!     ct.EndTimestamp = ucellp->expirationTime;
!     memcpy(cp, &ct, sizeof(ct));
!     cp += sizeof(ct);
! 
!     /* Primary flag (unused) */
!     temp = 0;
!     memcpy(cp, &temp, sizeof(temp));
!     cp += sizeof(temp);
! 
!     /* cell name */
!     strcpy(cp, ucellp->cellp->namep);
!     cp += strlen(cp) + 1;
! 
!     /* user name */
!     strcpy(cp, ucellp->userName);
!     cp += strlen(cp) + 1;
  
!     ioctlp->outDatap = cp;
  
!     lock_ReleaseMutex(&userp->mx);
  
  #ifndef DJGPP
!     cm_RegisterNewTokenEvent(uuid, ucellp->sessionKey.data);
  #endif /* !DJGPP */
  
!     return 0;
  }
  
  long cm_IoctlDelToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     char *cp;
!     cm_cell_t *cellp;
!     cm_ucell_t *ucellp;
  
!     cm_SkipIoctlPath(ioctlp);
  
!     cp = ioctlp->outDatap;
  
!     /* cell name is right here */
!     cellp = cm_GetCell(ioctlp->inDatap, 0);
!     if (!cellp) return CM_ERROR_NOSUCHCELL;
! 
!     lock_ObtainMutex(&userp->mx);
! 
!     ucellp = cm_GetUCell(userp, cellp);
!     if (!ucellp) {
!         lock_ReleaseMutex(&userp->mx);
!         return CM_ERROR_NOMORETOKENS;
!     }
  
      osi_Log1(smb_logp,"cm_IoctlDelToken ucellp %lx", ucellp);
  
!     if (ucellp->ticketp) {
!         free(ucellp->ticketp);
!         ucellp->ticketp = NULL;
!     }
!     ucellp->flags &= ~CM_UCELLFLAG_RXKAD;
!     ucellp->gen++;
  
!     lock_ReleaseMutex(&userp->mx);
  
!     cm_ResetACLCache(userp);
  
!     return 0;
  }
  
  long cm_IoctlDelAllToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
  {
!     cm_ucell_t *ucellp;
  
!     lock_ObtainMutex(&userp->mx);
  
      for (ucellp = userp->cellInfop; ucellp; ucellp = ucellp->nextp) {
          osi_Log1(smb_logp,"cm_IoctlDelAllToken ucellp %lx", ucellp);
!         ucellp->flags &= ~CM_UCELLFLAG_RXKAD;
!         ucellp->gen++;
!     }
  
!     lock_ReleaseMutex(&userp->mx);
  
!     cm_ResetACLCache(userp);
  
!     return 0;
  }
  
  long cm_IoctlMakeSubmount(smb_ioctl_t *ioctlp, cm_user_t *userp)
  {
!     char afspath[MAX_PATH];
!     char *submountreqp;
!     int nextAutoSubmount;
      HKEY hkSubmounts;
      DWORD dwType, dwSize;
      DWORD status;
      DWORD dwIndex;
      DWORD dwSubmounts;
  
!     cm_SkipIoctlPath(ioctlp);
! 
!     /* Serialize this one, to prevent simultaneous mods
!      * to afsdsbmt.ini
!      */
!     lock_ObtainMutex(&cm_Afsdsbmt_Lock);
! 
!     /* Parse the input parameters--first the required afs path,
!      * then the requested submount name (which may be "").
!      */
!     cm_NormalizeAfsPath (afspath, ioctlp->inDatap);
!     submountreqp = ioctlp->inDatap + (strlen(ioctlp->inDatap)+1);
  
!     /* If the caller supplied a suggested submount name, see if
!      * that submount name is in use... if so, the submount's path
!      * has to match our path.
!      */
  
      RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
                      "SOFTWARE\\OpenAFS\\Client\\Submounts",
***************
*** 2029,2050 ****
                      NULL );
  
      if (submountreqp && *submountreqp) {
! 		char submountPathNormalized[MAX_PATH];
! 		char submountPath[MAX_PATH];
  
          dwSize = sizeof(submountPath);
          status = RegQueryValueEx( hkSubmounts, submountreqp, 0,
!                          &dwType, submountPath, &dwSize);
  
! 		if (status != ERROR_SUCCESS) {
  
! 			/* The suggested submount name isn't in use now--
! 			 * so we can safely map the requested submount name
! 			 * to the supplied path. Remember not to write the
! 			 * leading "/afs" when writing out the submount.
! 			 */
              RegSetValueEx( hkSubmounts, submountreqp, 0,
!                            REG_SZ, 
                             (strlen(&afspath[strlen(cm_mountRoot)])) ?
                             &afspath[strlen(cm_mountRoot)]:"/",
                             (strlen(&afspath[strlen(cm_mountRoot)])) ?
--- 2058,2079 ----
                      NULL );
  
      if (submountreqp && *submountreqp) {
!         char submountPathNormalized[MAX_PATH];
!         char submountPath[MAX_PATH];
  
          dwSize = sizeof(submountPath);
          status = RegQueryValueEx( hkSubmounts, submountreqp, 0,
!                                   &dwType, submountPath, &dwSize);
  
!         if (status != ERROR_SUCCESS) {
  
!             /* The suggested submount name isn't in use now--
!              * so we can safely map the requested submount name
!              * to the supplied path. Remember not to write the
!              * leading "/afs" when writing out the submount.
!              */
              RegSetValueEx( hkSubmounts, submountreqp, 0,
!                            REG_EXPAND_SZ, 
                             (strlen(&afspath[strlen(cm_mountRoot)])) ?
                             &afspath[strlen(cm_mountRoot)]:"/",
                             (strlen(&afspath[strlen(cm_mountRoot)])) ?
***************
*** 2055,2177 ****
  			ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
  			lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
              return 0;
! 		}
  
! 		/* The suggested submount name is already in use--if the
! 		 * supplied path matches the submount's path, we can still
! 		 * use the suggested submount name.
! 		 */
! 		cm_NormalizeAfsPath (submountPathNormalized, submountPath);
! 		if (!strcmp (submountPathNormalized, afspath)) {
! 			strcpy(ioctlp->outDatap, submountreqp);
! 			ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
              RegCloseKey( hkSubmounts );
! 			lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
              return 0;
! 		}
! 	}
  
      RegQueryInfoKey( hkSubmounts,
!                  NULL,  /* lpClass */
!                  NULL,  /* lpcClass */
!                  NULL,  /* lpReserved */
!                  NULL,  /* lpcSubKeys */
!                  NULL,  /* lpcMaxSubKeyLen */
!                  NULL,  /* lpcMaxClassLen */
!                  &dwSubmounts, /* lpcValues */
!                  NULL,  /* lpcMaxValueNameLen */
!                  NULL,  /* lpcMaxValueLen */
!                  NULL,  /* lpcbSecurityDescriptor */
!                  NULL   /* lpftLastWriteTime */
!                  );
! 
! 
! 	/* Having obtained a list of all available submounts, start
! 	 * searching that list for a path which matches the requested
! 	 * AFS path. We'll also keep track of the highest "auto15"/"auto47"
! 	 * submount, in case we need to add a new one later.
! 	 */
  
! 	nextAutoSubmount = 1;
  
      for ( dwIndex = 0; dwIndex < dwSubmounts; dwIndex ++ ) {
! 		char submountPathNormalized[MAX_PATH];
! 		char submountPath[MAX_PATH] = "";
! 		DWORD submountPathLen = sizeof(submountPath);
!         char submountName[256];
          DWORD submountNameLen = sizeof(submountName);
  
          RegEnumValue( hkSubmounts, dwIndex, submountName, &submountNameLen, NULL,
!               &dwType, submountPath, &submountPathLen);
  
! 		/* If this is an Auto### submount, remember its ### value */
  
! 		if ((!strnicmp (submountName, "auto", 4)) &&
! 		    (isdigit (submountName[strlen("auto")]))) {
! 			int thisAutoSubmount;
! 			thisAutoSubmount = atoi (&submountName[strlen("auto")]);
! 			nextAutoSubmount = max (nextAutoSubmount,
! 						thisAutoSubmount+1);
! 		}
! 
! 		if ((submountPathLen == 0) ||
! 		    (submountPathLen == sizeof(submountPath) - 1)) {
! 			continue;
! 		}
! 
! 		/* See if the path for this submount matches the path
! 		 * that our caller specified. If so, we can return
! 		 * this submount.
! 		 */
! 		cm_NormalizeAfsPath (submountPathNormalized, submountPath);
! 		if (!strcmp (submountPathNormalized, afspath)) {
! 			strcpy(ioctlp->outDatap, submountName);
! 			ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
              RegCloseKey(hkSubmounts);
! 			lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
              return 0;
  
! 		}
! 	}
  
! 	/* We've been through the entire list of existing submounts, and
! 	 * didn't find any which matched the specified path. So, we'll
! 	 * just have to add one. Remember not to write the leading "/afs"
! 	 * when writing out the submount.
! 	 */
  
! 	sprintf(ioctlp->outDatap, "auto%ld", nextAutoSubmount);
  
      RegSetValueEx( hkSubmounts, 
                     ioctlp->outDatap,
                     0,
!                    REG_SZ, 
                     (strlen(&afspath[strlen(cm_mountRoot)])) ?
                     &afspath[strlen(cm_mountRoot)]:"/",
                     (strlen(&afspath[strlen(cm_mountRoot)])) ?
                     strlen(&afspath[strlen(cm_mountRoot)])+1:2);
  
! 	ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
      RegCloseKey(hkSubmounts);
! 	lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
! 	return 0;
  }
  
  long cm_IoctlGetRxkcrypt(smb_ioctl_t *ioctlp, cm_user_t *userp)
  {
! 	memcpy(ioctlp->outDatap, &cryptall, sizeof(cryptall));
!         ioctlp->outDatap += sizeof(cryptall);
  
! 	return 0;
  }
  
  long cm_IoctlSetRxkcrypt(smb_ioctl_t *ioctlp, cm_user_t *userp)
  {
! 	cm_SkipIoctlPath(ioctlp);
  
! 	memcpy(&cryptall, ioctlp->inDatap, sizeof(cryptall));
  
! 	return 0;
  }
  
  #ifdef DJGPP
--- 2084,2213 ----
  			ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
  			lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
              return 0;
!         }
  
!         /* The suggested submount name is already in use--if the
!          * supplied path matches the submount's path, we can still
!          * use the suggested submount name.
!          */
!         cm_NormalizeAfsPath (submountPathNormalized, submountPath);
!         if (!strcmp (submountPathNormalized, afspath)) {
!             strcpy(ioctlp->outDatap, submountreqp);
!             ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
              RegCloseKey( hkSubmounts );
!             lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
              return 0;
!         }
!     }
  
      RegQueryInfoKey( hkSubmounts,
!                      NULL,  /* lpClass */
!                      NULL,  /* lpcClass */
!                      NULL,  /* lpReserved */
!                      NULL,  /* lpcSubKeys */
!                      NULL,  /* lpcMaxSubKeyLen */
!                      NULL,  /* lpcMaxClassLen */
!                      &dwSubmounts, /* lpcValues */
!                      NULL,  /* lpcMaxValueNameLen */
!                      NULL,  /* lpcMaxValueLen */
!                      NULL,  /* lpcbSecurityDescriptor */
!                      NULL   /* lpftLastWriteTime */
!                      );
! 
! 
!     /* Having obtained a list of all available submounts, start
!      * searching that list for a path which matches the requested
!      * AFS path. We'll also keep track of the highest "auto15"/"auto47"
!      * submount, in case we need to add a new one later.
!      */
  
!     nextAutoSubmount = 1;
  
      for ( dwIndex = 0; dwIndex < dwSubmounts; dwIndex ++ ) {
!         char submountPathNormalized[MAX_PATH];
!         char submountPath[MAX_PATH] = "";
!         DWORD submountPathLen = sizeof(submountPath);
!         char submountName[MAX_PATH];
          DWORD submountNameLen = sizeof(submountName);
  
+         dwType = 0;
          RegEnumValue( hkSubmounts, dwIndex, submountName, &submountNameLen, NULL,
!                       &dwType, submountPath, &submountPathLen);
!         if (dwType == REG_EXPAND_SZ) {
!             char buf[MAX_PATH];
!             StringCbCopyA(buf, MAX_PATH, submountPath);
!             submountPathLen = ExpandEnvironmentStrings(buf, submountPath, MAX_PATH);
!             if (submountPathLen > MAX_PATH)
!                 continue;
!         }
  
!         /* If this is an Auto### submount, remember its ### value */
!         if ((!strnicmp (submountName, "auto", 4)) &&
!              (isdigit (submountName[strlen("auto")]))) {
!             int thisAutoSubmount;
!             thisAutoSubmount = atoi (&submountName[strlen("auto")]);
!             nextAutoSubmount = max (nextAutoSubmount,
!                                      thisAutoSubmount+1);
!         }       
! 
!         if ((submountPathLen == 0) ||
!              (submountPathLen == sizeof(submountPath) - 1)) {
!             continue;
!         }
  
!         /* See if the path for this submount matches the path
!          * that our caller specified. If so, we can return
!          * this submount.
!          */
!         cm_NormalizeAfsPath (submountPathNormalized, submountPath);
!         if (!strcmp (submountPathNormalized, afspath)) {
!             strcpy(ioctlp->outDatap, submountName);
!             ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
              RegCloseKey(hkSubmounts);
!             lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
              return 0;
  
!         }
!     }
  
!     /* We've been through the entire list of existing submounts, and
!      * didn't find any which matched the specified path. So, we'll
!      * just have to add one. Remember not to write the leading "/afs"
!      * when writing out the submount.
!      */
  
!     sprintf(ioctlp->outDatap, "auto%ld", nextAutoSubmount);
  
      RegSetValueEx( hkSubmounts, 
                     ioctlp->outDatap,
                     0,
!                    REG_EXPAND_SZ, 
                     (strlen(&afspath[strlen(cm_mountRoot)])) ?
                     &afspath[strlen(cm_mountRoot)]:"/",
                     (strlen(&afspath[strlen(cm_mountRoot)])) ?
                     strlen(&afspath[strlen(cm_mountRoot)])+1:2);
  
!     ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
      RegCloseKey(hkSubmounts);
!     lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
!     return 0;
  }
  
  long cm_IoctlGetRxkcrypt(smb_ioctl_t *ioctlp, cm_user_t *userp)
  {
!     memcpy(ioctlp->outDatap, &cryptall, sizeof(cryptall));
!     ioctlp->outDatap += sizeof(cryptall);
  
!     return 0;
  }
  
  long cm_IoctlSetRxkcrypt(smb_ioctl_t *ioctlp, cm_user_t *userp)
  {
!     cm_SkipIoctlPath(ioctlp);
  
!     memcpy(&cryptall, ioctlp->inDatap, sizeof(cryptall));
  
!     return 0;
  }
  
  #ifdef DJGPP
***************
*** 2191,2197 ****
    if (uidp && uidp->unp) {
      memcpy(ioctlp->outDatap, uidp->unp->name, strlen(uidp->unp->name));
      ioctlp->outDatap += strlen(uidp->unp->name);
! 	}
  
    return 0;
  }
--- 2227,2233 ----
    if (uidp && uidp->unp) {
      memcpy(ioctlp->outDatap, uidp->unp->name, strlen(uidp->unp->name));
      ioctlp->outDatap += strlen(uidp->unp->name);
!   }
  
    return 0;
  }
Index: openafs/src/WINNT/afsd/cm_ioctl.h
diff -c openafs/src/WINNT/afsd/cm_ioctl.h:1.8 openafs/src/WINNT/afsd/cm_ioctl.h:1.8.2.1
*** openafs/src/WINNT/afsd/cm_ioctl.h:1.8	Sat Jun 19 12:00:13 2004
--- openafs/src/WINNT/afsd/cm_ioctl.h	Sun Oct 10 19:52:05 2004
***************
*** 43,51 ****
  
  #define MAXNUMSYSNAMES    16      /* max that current constants allow */
  #define   MAXSYSNAME      128     /* max sysname (i.e. @sys) size */
! extern char *cm_sysName;
! extern int   cm_sysNameCount;
! extern char *cm_sysNameList[MAXNUMSYSNAMES];
  
  #ifndef __CM_IOCTL_INTERFACES_ONLY__
  
--- 43,51 ----
  
  #define MAXNUMSYSNAMES    16      /* max that current constants allow */
  #define   MAXSYSNAME      128     /* max sysname (i.e. @sys) size */
! extern char *         cm_sysName;
! extern unsigned int   cm_sysNameCount;
! extern char *         cm_sysNameList[MAXNUMSYSNAMES];
  
  #ifndef __CM_IOCTL_INTERFACES_ONLY__
  
Index: openafs/src/WINNT/afsd/cm_scache.h
diff -c openafs/src/WINNT/afsd/cm_scache.h:1.4.2.1 openafs/src/WINNT/afsd/cm_scache.h:1.4.2.2
*** openafs/src/WINNT/afsd/cm_scache.h:1.4.2.1	Wed Aug 18 13:11:22 2004
--- openafs/src/WINNT/afsd/cm_scache.h	Mon Oct 18 00:09:26 2004
***************
*** 58,64 ****
          				 * write-locked to prevent buffers from
                                           * being created during a truncate op, etc.
                                           */
!         int refCount;			/* reference count; cm_scacheLock */
          osi_queueData_t *bufReadsp;	/* queue of buffers being read */
          osi_queueData_t *bufWritesp;	/* queue of buffers being written */
  
--- 58,64 ----
          				 * write-locked to prevent buffers from
                                           * being created during a truncate op, etc.
                                           */
!         unsigned long refCount;			/* reference count; cm_scacheLock */
          osi_queueData_t *bufReadsp;	/* queue of buffers being read */
          osi_queueData_t *bufWritesp;	/* queue of buffers being written */
  
Index: openafs/src/WINNT/afsd/cm_server.c
diff -c openafs/src/WINNT/afsd/cm_server.c:1.13.2.2 openafs/src/WINNT/afsd/cm_server.c:1.13.2.4
*** openafs/src/WINNT/afsd/cm_server.c:1.13.2.2	Tue Aug 17 11:26:04 2004
--- openafs/src/WINNT/afsd/cm_server.c	Mon Oct 18 00:09:26 2004
***************
*** 37,138 ****
  
  void cm_CheckServers(long flags, cm_cell_t *cellp)
  {
! 	/* ping all file servers, up or down, with unauthenticated connection,
!          * to find out whether we have all our callbacks from the server still.
!          * Also, ping down VLDBs.
!          */
!         cm_server_t *tsp;
!         long code;
!         long secs;
!         long usecs;
! 	int doPing;
!         int serverType;
!         long now;
! 	int wasDown;
!         cm_conn_t *connp;
  
!         lock_ObtainWrite(&cm_serverLock);
! 	for(tsp = cm_allServersp; tsp; tsp = tsp->allNextp) {
          cm_GetServerNoLock(tsp);
!                 lock_ReleaseWrite(&cm_serverLock);
  
! 		/* now process the server */
!                 lock_ObtainMutex(&tsp->mx);
  
! 		/* what time is it? */
!                 now = osi_Time();
  
! 		serverType = tsp->type;
! 		doPing = 0;
!                 wasDown = tsp->flags & CM_SERVERFLAG_DOWN;
  
! 		/* only do the ping if the cell matches the requested cell, or we're
!                  * matching all cells (cellp == NULL), and if we've requested to ping
!                  * this type of {up, down} servers.
!                  */
! 		if ((cellp == NULL || cellp == tsp->cellp) &&
! 			((wasDown && (flags & CM_FLAG_CHECKDOWNSERVERS)) ||
!                 	 (!wasDown && (flags & CM_FLAG_CHECKUPSERVERS)))) {
! 
! 			doPing = 1;
! 		}	/* we're supposed to check this up/down server */
!                 lock_ReleaseMutex(&tsp->mx);
!                         
!                 /* at this point, we've adjusted the server state, so do the ping and
!                  * adjust things.
                   */
!                 if (doPing) {
! 			code = cm_ConnByServer(tsp, cm_rootUserp, &connp);
!                         if (code == 0) {
! 				/* now call the appropriate ping call.  Drop the timeout if
! 	                         * the server is known to be down, so that we don't waste a
! 	                         * lot of time retiming out down servers.
! 	                         */
! 				if (wasDown)
! 					rx_SetConnDeadTime(connp->callp, 10);
! 	                        if (serverType == CM_SERVER_VLDB) {
! 					code = VL_ProbeServer(connp->callp);
! 	                        }
! 	                        else {
! 					/* file server */
! 	                                code = RXAFS_GetTime(connp->callp, &secs, &usecs);
! 	                        }
! 				if (wasDown)
! 					rx_SetConnDeadTime(connp->callp, ConnDeadtimeout);
! 	                        cm_PutConn(connp);
! 			}	/* got an unauthenticated connection to this server */
! 
! 			lock_ObtainMutex(&tsp->mx);
! 			if (code == 0) {
! 				/* mark server as up */
!                                 tsp->flags &= ~CM_SERVERFLAG_DOWN;
!                         }
!                         else {
!                                	/* mark server as down */
!                                 tsp->flags |= CM_SERVERFLAG_DOWN;
! 			}
! 			lock_ReleaseMutex(&tsp->mx);
                  }
!                         
!                 /* also, run the GC function for connections on all of the
!                  * server's connections.
!                  */
! 		cm_GCConnections(tsp);
  
!                 lock_ObtainWrite(&cm_serverLock);
          cm_PutServerNoLock(tsp);
!         }
!         lock_ReleaseWrite(&cm_serverLock);
! }
  
  void cm_InitServer(void)
  {
! 	static osi_once_t once;
          
!         if (osi_Once(&once)) {
! 		lock_InitializeRWLock(&cm_serverLock, "cm_serverLock");
! 		osi_EndOnce(&once);
!         }
  }
  
  void cm_GetServer(cm_server_t *serverp)
--- 37,141 ----
  
  void cm_CheckServers(long flags, cm_cell_t *cellp)
  {
!     /* ping all file servers, up or down, with unauthenticated connection,
!      * to find out whether we have all our callbacks from the server still.
!      * Also, ping down VLDBs.
!      */
!     cm_server_t *tsp;
!     long code;
!     long secs;
!     long usecs;
!     int doPing;
!     int serverType;
!     long now;
!     int wasDown;
!     cm_conn_t *connp;
!     struct rx_connection * callp;
  
!     lock_ObtainWrite(&cm_serverLock);
!     for (tsp = cm_allServersp; tsp; tsp = tsp->allNextp) {
          cm_GetServerNoLock(tsp);
!         lock_ReleaseWrite(&cm_serverLock);
  
!         /* now process the server */
!         lock_ObtainMutex(&tsp->mx);
  
!         /* what time is it? */
!         now = osi_Time();
  
!         serverType = tsp->type;
!         doPing = 0;
!         wasDown = tsp->flags & CM_SERVERFLAG_DOWN;
! 
!         /* only do the ping if the cell matches the requested cell, or we're
!          * matching all cells (cellp == NULL), and if we've requested to ping
!          * this type of {up, down} servers.
!          */
!         if ((cellp == NULL || cellp == tsp->cellp) &&
!              ((wasDown && (flags & CM_FLAG_CHECKDOWNSERVERS)) ||
!                (!wasDown && (flags & CM_FLAG_CHECKUPSERVERS)))) {
! 
!             doPing = 1;
!         }	/* we're supposed to check this up/down server */
!         lock_ReleaseMutex(&tsp->mx);
  
!         /* at this point, we've adjusted the server state, so do the ping and
!          * adjust things.
!          */
!         if (doPing) {
!             code = cm_ConnByServer(tsp, cm_rootUserp, &connp);
!             if (code == 0) {
!                 /* now call the appropriate ping call.  Drop the timeout if
!                  * the server is known to be down, so that we don't waste a
!                  * lot of time retiming out down servers.
                   */
!                 if (wasDown)
!                     rx_SetConnDeadTime(connp->callp, 10);
!                 if (serverType == CM_SERVER_VLDB) {
!                     code = VL_ProbeServer(connp->callp);
                  }
!                 else {
!                     /* file server */
!                     callp = cm_GetRxConn(connp);
!                     code = RXAFS_GetTime(callp, &secs, &usecs);
!                     rx_PutConnection(callp);
!                 }
!                 if (wasDown)
!                     rx_SetConnDeadTime(connp->callp, ConnDeadtimeout);
!                 cm_PutConn(connp);
!             }	/* got an unauthenticated connection to this server */
! 
!             lock_ObtainMutex(&tsp->mx);
!             if (code == 0) {
!                 /* mark server as up */
!                 tsp->flags &= ~CM_SERVERFLAG_DOWN;
!             }
!             else {
!                 /* mark server as down */
!                 tsp->flags |= CM_SERVERFLAG_DOWN;
!             }
!             lock_ReleaseMutex(&tsp->mx);
!         }
! 
!         /* also, run the GC function for connections on all of the
!          * server's connections.
!          */
!         cm_GCConnections(tsp);
  
!         lock_ObtainWrite(&cm_serverLock);
          cm_PutServerNoLock(tsp);
!     }
!     lock_ReleaseWrite(&cm_serverLock);
! }       
  
  void cm_InitServer(void)
  {
!     static osi_once_t once;
          
!     if (osi_Once(&once)) {
!         lock_InitializeRWLock(&cm_serverLock, "cm_serverLock");
!         osi_EndOnce(&once);
!     }
  }
  
  void cm_GetServer(cm_server_t *serverp)
***************
*** 149,233 ****
  
  void cm_PutServer(cm_server_t *serverp)
  {
! 	lock_ObtainWrite(&cm_serverLock);
! 	osi_assert(serverp->refCount-- > 0);
! 	lock_ReleaseWrite(&cm_serverLock);
  }
  
  void cm_PutServerNoLock(cm_server_t *serverp)
  {
! 	osi_assert(serverp->refCount-- > 0);
  }
  
  void cm_SetServerPrefs(cm_server_t * serverp)
  {
! 	unsigned long	serverAddr; 	/* in host byte order */
! 	unsigned long	myAddr, myNet, mySubnet;/* in host byte order */
! 	unsigned long	netMask;
! 	int 		i;
! 
! 	/* implement server prefs for fileservers only */
! 	if ( serverp->type == CM_SERVER_FILE )
! 	{
! 	    serverAddr = ntohl(serverp->addr.sin_addr.s_addr);
! 	    serverp->ipRank  = CM_IPRANK_LOW;	/* default setings */
! 
! 	    for ( i=0; i < cm_noIPAddr; i++)
! 	    {
! 		/* loop through all the client's IP address and compare
! 		** each of them against the server's IP address */
! 
! 		myAddr = cm_IPAddr[i];
! 		if ( IN_CLASSA(myAddr) )
! 		    netMask = IN_CLASSA_NET;
! 		else if ( IN_CLASSB(myAddr) )
! 		    netMask = IN_CLASSB_NET;
! 		else if ( IN_CLASSC(myAddr) )
! 		    netMask = IN_CLASSC_NET;
! 		else
! 		    netMask = 0;
! 
! 		myNet    =  myAddr & netMask;
! 		mySubnet =  myAddr & cm_SubnetMask[i];
! 
! 		if ( (serverAddr & netMask) == myNet ) 
! 		{
! 		    if ( (serverAddr & cm_SubnetMask[i]) == mySubnet)
! 		    {
! 			if ( serverAddr == myAddr ) 
! 			    serverp->ipRank = min(serverp->ipRank,
! 					      CM_IPRANK_TOP);/* same machine */
! 			else serverp->ipRank = min(serverp->ipRank,
! 					      CM_IPRANK_HI); /* same subnet */
! 		    }
! 		    else serverp->ipRank = min(serverp->ipRank,CM_IPRANK_MED);
! 							   /* same net */
! 		}	
! 						 /* random between 0..15*/
! 		serverp->ipRank += min(serverp->ipRank, rand() % 0x000f);
! 	    } /* and of for loop */
! 	}
      else 
          serverp->ipRank = 10000 + (rand() % 0x00ff); /* VL server */
  }
  
  cm_server_t *cm_NewServer(struct sockaddr_in *socketp, int type, cm_cell_t *cellp) {
! 	cm_server_t *tsp;
  
! 	osi_assert(socketp->sin_family == AF_INET);
  
! 	tsp = malloc(sizeof(*tsp));
      memset(tsp, 0, sizeof(*tsp));
! 	tsp->type = type;
      tsp->cellp = cellp;
      tsp->refCount = 1;
! 	lock_InitializeMutex(&tsp->mx, "cm_server_t mutex");
! 	tsp->addr = *socketp;
  
! 	cm_SetServerPrefs(tsp); 
  
      lock_ObtainWrite(&cm_serverLock); /* get server lock */
! 	tsp->allNextp = cm_allServersp;
      cm_allServersp = tsp;
      lock_ReleaseWrite(&cm_serverLock); /* release server lock */
  
--- 152,236 ----
  
  void cm_PutServer(cm_server_t *serverp)
  {
!     lock_ObtainWrite(&cm_serverLock);
!     osi_assert(serverp->refCount-- > 0);
!     lock_ReleaseWrite(&cm_serverLock);
  }
  
  void cm_PutServerNoLock(cm_server_t *serverp)
  {
!     osi_assert(serverp->refCount-- > 0);
  }
  
  void cm_SetServerPrefs(cm_server_t * serverp)
  {
!     unsigned long	serverAddr; 	/* in host byte order */
!     unsigned long	myAddr, myNet, mySubnet;/* in host byte order */
!     unsigned long	netMask;
!     int 		i;
! 
!     /* implement server prefs for fileservers only */
!     if ( serverp->type == CM_SERVER_FILE )
!     {
!         serverAddr = ntohl(serverp->addr.sin_addr.s_addr);
!         serverp->ipRank  = CM_IPRANK_LOW;	/* default setings */
! 
!         for ( i=0; i < cm_noIPAddr; i++)
!         {
!             /* loop through all the client's IP address and compare
!             ** each of them against the server's IP address */
! 
!             myAddr = cm_IPAddr[i];
!             if ( IN_CLASSA(myAddr) )
!                 netMask = IN_CLASSA_NET;
!             else if ( IN_CLASSB(myAddr) )
!                 netMask = IN_CLASSB_NET;
!             else if ( IN_CLASSC(myAddr) )
!                 netMask = IN_CLASSC_NET;
!             else
!                 netMask = 0;
! 
!             myNet    =  myAddr & netMask;
!             mySubnet =  myAddr & cm_SubnetMask[i];
! 
!             if ( (serverAddr & netMask) == myNet ) 
!             {
!                 if ( (serverAddr & cm_SubnetMask[i]) == mySubnet)
!                 {
!                     if ( serverAddr == myAddr ) 
!                         serverp->ipRank = min(serverp->ipRank,
!                                                CM_IPRANK_TOP);/* same machine */
!                     else serverp->ipRank = min(serverp->ipRank,
!                                                 CM_IPRANK_HI); /* same subnet */
!                 }
!                 else serverp->ipRank = min(serverp->ipRank,CM_IPRANK_MED);
!                 /* same net */
!             }	
!             /* random between 0..15*/
!             serverp->ipRank += min(serverp->ipRank, rand() % 0x000f);
!         } /* and of for loop */
!     }
      else 
          serverp->ipRank = 10000 + (rand() % 0x00ff); /* VL server */
  }
  
  cm_server_t *cm_NewServer(struct sockaddr_in *socketp, int type, cm_cell_t *cellp) {
!     cm_server_t *tsp;
  
!     osi_assert(socketp->sin_family == AF_INET);
  
!     tsp = malloc(sizeof(*tsp));
      memset(tsp, 0, sizeof(*tsp));
!     tsp->type = type;
      tsp->cellp = cellp;
      tsp->refCount = 1;
!     lock_InitializeMutex(&tsp->mx, "cm_server_t mutex");
!     tsp->addr = *socketp;
  
!     cm_SetServerPrefs(tsp); 
  
      lock_ObtainWrite(&cm_serverLock); /* get server lock */
!     tsp->allNextp = cm_allServersp;
      cm_allServersp = tsp;
      lock_ReleaseWrite(&cm_serverLock); /* release server lock */
  
***************
*** 237,294 ****
  /* find a server based on its properties */
  cm_server_t *cm_FindServer(struct sockaddr_in *addrp, int type)
  {
! 	cm_server_t *tsp;
  
! 	osi_assert(addrp->sin_family == AF_INET);
          
!         lock_ObtainWrite(&cm_serverLock);
! 	for(tsp = cm_allServersp; tsp; tsp=tsp->allNextp) {
! 		if (tsp->type == type &&
!                 	tsp->addr.sin_addr.s_addr == addrp->sin_addr.s_addr) break;
!         }
  
! 	/* bump ref count if we found the server */
      if (tsp) 
          cm_GetServerNoLock(tsp);
  
! 	/* drop big table lock */
!         lock_ReleaseWrite(&cm_serverLock);
  	
! 	/* return what we found */
!         return tsp;
! }
  
  cm_serverRef_t *cm_NewServerRef(cm_server_t *serverp)
  {
! 	cm_serverRef_t *tsrp;
  
      cm_GetServer(serverp);
! 	tsrp = malloc(sizeof(*tsrp));
! 	tsrp->server = serverp;
! 	tsrp->status = not_busy;
! 	tsrp->next = NULL;
      tsrp->refCount = 1;
  
! 	return tsrp;
  }
  
  long cm_ChecksumServerList(cm_serverRef_t *serversp)
  {
! 	long sum = 0;
! 	int first = 1;
! 	cm_serverRef_t *tsrp;
  
      lock_ObtainWrite(&cm_serverLock);
! 	for (tsrp = serversp; tsrp; tsrp=tsrp->next) {
! 		if (first)
! 			first = 0;
! 		else
! 			sum <<= 1;
! 		sum ^= (long) tsrp->server;
! 	}
  
      lock_ReleaseWrite(&cm_serverLock);
! 	return sum;
  }
  
  /*
--- 240,297 ----
  /* find a server based on its properties */
  cm_server_t *cm_FindServer(struct sockaddr_in *addrp, int type)
  {
!     cm_server_t *tsp;
  
!     osi_assert(addrp->sin_family == AF_INET);
          
!     lock_ObtainWrite(&cm_serverLock);
!     for (tsp = cm_allServersp; tsp; tsp=tsp->allNextp) {
!         if (tsp->type == type &&
!              tsp->addr.sin_addr.s_addr == addrp->sin_addr.s_addr) break;
!     }       
  
!     /* bump ref count if we found the server */
      if (tsp) 
          cm_GetServerNoLock(tsp);
  
!     /* drop big table lock */
!     lock_ReleaseWrite(&cm_serverLock);
  	
!     /* return what we found */
!     return tsp;
! }       
  
  cm_serverRef_t *cm_NewServerRef(cm_server_t *serverp)
  {
!     cm_serverRef_t *tsrp;
  
      cm_GetServer(serverp);
!     tsrp = malloc(sizeof(*tsrp));
!     tsrp->server = serverp;
!     tsrp->status = not_busy;
!     tsrp->next = NULL;
      tsrp->refCount = 1;
  
!     return tsrp;
  }
  
  long cm_ChecksumServerList(cm_serverRef_t *serversp)
  {
!     long sum = 0;
!     int first = 1;
!     cm_serverRef_t *tsrp;
  
      lock_ObtainWrite(&cm_serverLock);
!     for (tsrp = serversp; tsrp; tsrp=tsrp->next) {
!         if (first)
!             first = 0;
!         else
!             sum <<= 1;
!         sum ^= (long) tsrp->server;
!     }
  
      lock_ReleaseWrite(&cm_serverLock);
!     return sum;
  }
  
  /*
***************
*** 299,329 ****
  */
  void cm_InsertServerList(cm_serverRef_t** list, cm_serverRef_t* element)
  {
! 	cm_serverRef_t	*current=*list;
! 	unsigned short ipRank = element->server->ipRank;
  
      lock_ObtainWrite(&cm_serverLock);
      element->refCount++;                /* increase refCount */
  
      /* insertion into empty list  or at the beginning of the list */
! 	if ( !current || (current->server->ipRank > ipRank) )
! 	{
! 		element->next = *list;
! 		*list = element;
          lock_ReleaseWrite(&cm_serverLock);
! 		return ;	
! 	}
  	
! 	while ( current->next ) /* find appropriate place to insert */
! 	{
! 		if ( current->next->server->ipRank > ipRank )
! 			break;
! 		else current = current->next;
! 	}
! 	element->next = current->next;
! 	current->next = element;
      lock_ReleaseWrite(&cm_serverLock);
! }
  /*
  ** Re-sort the server list with the modified rank
  ** returns 0 if element was changed successfully. 
--- 302,332 ----
  */
  void cm_InsertServerList(cm_serverRef_t** list, cm_serverRef_t* element)
  {
!     cm_serverRef_t	*current=*list;
!     unsigned short ipRank = element->server->ipRank;
  
      lock_ObtainWrite(&cm_serverLock);
      element->refCount++;                /* increase refCount */
  
      /* insertion into empty list  or at the beginning of the list */
!     if ( !current || (current->server->ipRank > ipRank) )
!     {
!         element->next = *list;
!         *list = element;
          lock_ReleaseWrite(&cm_serverLock);
!         return ;	
!     }
  	
!     while ( current->next ) /* find appropriate place to insert */
!     {
!         if ( current->next->server->ipRank > ipRank )
!             break;
!         else current = current->next;
!     }
!     element->next = current->next;
!     current->next = element;
      lock_ReleaseWrite(&cm_serverLock);
! }       
  /*
  ** Re-sort the server list with the modified rank
  ** returns 0 if element was changed successfully. 
***************
*** 331,369 ****
  */
  long cm_ChangeRankServer(cm_serverRef_t** list, cm_server_t*	server)
  {
! 	cm_serverRef_t  **current=list;
! 	cm_serverRef_t	*element=0;
  
! 	/* if there is max of one element in the list, nothing to sort */
! 	if ( (!*current) || !((*current)->next)  )
! 		return 1;		/* list unchanged: return success */
  
      lock_ObtainWrite(&cm_serverLock);
! 	/* if the server is on the list, delete it from list */
! 	while ( *current )
! 	{
! 		if ( (*current)->server == server)
! 		{
! 			element = (*current);
! 			*current = (*current)->next; /* delete it */
! 			break;
! 		}
! 		current = & ( (*current)->next);	
! 	}
      lock_ReleaseWrite(&cm_serverLock);
  
      /* if this volume is not replicated on this server  */
! 	if (!element)
! 		return 1;	/* server is not on list */
  
! 	/* re-insert deleted element into the list with modified rank*/
! 	cm_InsertServerList(list, element);
  
      /* reduce refCount which was increased by cm_InsertServerList */
      lock_ObtainWrite(&cm_serverLock);
      element->refCount--;
      lock_ReleaseWrite(&cm_serverLock);
! 	return 0;
  }
  /*
  ** If there are more than one server on the list and the first n servers on 
--- 334,372 ----
  */
  long cm_ChangeRankServer(cm_serverRef_t** list, cm_server_t*	server)
  {
!     cm_serverRef_t  **current=list;
!     cm_serverRef_t	*element=0;
  
!     /* if there is max of one element in the list, nothing to sort */
!     if ( (!*current) || !((*current)->next)  )
!         return 1;		/* list unchanged: return success */
  
      lock_ObtainWrite(&cm_serverLock);
!     /* if the server is on the list, delete it from list */
!     while ( *current )
!     {
!         if ( (*current)->server == server)
!         {
!             element = (*current);
!             *current = (*current)->next; /* delete it */
!             break;
!         }
!         current = & ( (*current)->next);	
!     }
      lock_ReleaseWrite(&cm_serverLock);
  
      /* if this volume is not replicated on this server  */
!     if (!element)
!         return 1;	/* server is not on list */
  
!     /* re-insert deleted element into the list with modified rank*/
!     cm_InsertServerList(list, element);
  
      /* reduce refCount which was increased by cm_InsertServerList */
      lock_ObtainWrite(&cm_serverLock);
      element->refCount--;
      lock_ReleaseWrite(&cm_serverLock);
!     return 0;
  }
  /*
  ** If there are more than one server on the list and the first n servers on 
***************
*** 371,419 ****
  */
  void cm_RandomizeServer(cm_serverRef_t** list)
  {
! 	int 		count, picked;
! 	cm_serverRef_t*	tsrp = *list, *lastTsrp;
! 	unsigned short	lowestRank;
! 
! 	/* an empty list or a list with only one element */
! 	if ( !tsrp || ! tsrp->next )
! 		return ; 
  
      lock_ObtainWrite(&cm_serverLock);
  
! 	/* count the number of servers with the lowest rank */
! 	lowestRank = tsrp->server->ipRank;
! 	for ( count=1, tsrp=tsrp->next; tsrp; tsrp=tsrp->next)
! 	{
! 		if ( tsrp->server->ipRank != lowestRank)
! 			break;
! 		else
! 			count++;
! 	}	
  
! 	/* if there is only one server with the lowest rank, we are done */
! 	if ( count <= 1 ) {
          lock_ReleaseWrite(&cm_serverLock);
! 		return ;
!     }
  
! 	picked = rand() % count;
! 	if ( !picked ) {
          lock_ReleaseWrite(&cm_serverLock);
! 		return ;
!     }
  
! 	tsrp = *list;
! 	while (--picked >= 0)
! 	{
! 		lastTsrp = tsrp;
! 		tsrp = tsrp->next;
! 	}
! 	lastTsrp->next = tsrp->next;  /* delete random element from list*/
! 	tsrp->next     = *list; /* insert element at the beginning of list */
! 	*list          = tsrp;
      lock_ReleaseWrite(&cm_serverLock);
! }
  
  /* call cm_FreeServer while holding a write lock on cm_serverLock */
  void cm_FreeServer(cm_server_t* serverp)
--- 374,422 ----
  */
  void cm_RandomizeServer(cm_serverRef_t** list)
  {
!     int 		count, picked;
!     cm_serverRef_t*	tsrp = *list, *lastTsrp;
!     unsigned short	lowestRank;
! 
!     /* an empty list or a list with only one element */
!     if ( !tsrp || ! tsrp->next )
!         return ; 
  
      lock_ObtainWrite(&cm_serverLock);
  
!     /* count the number of servers with the lowest rank */
!     lowestRank = tsrp->server->ipRank;
!     for ( count=1, tsrp=tsrp->next; tsrp; tsrp=tsrp->next)
!     {
!         if ( tsrp->server->ipRank != lowestRank)
!             break;
!         else
!             count++;
!     }       	
  
!     /* if there is only one server with the lowest rank, we are done */
!     if ( count <= 1 ) {
          lock_ReleaseWrite(&cm_serverLock);
!         return ;
!     }   
  
!     picked = rand() % count;
!     if ( !picked ) {
          lock_ReleaseWrite(&cm_serverLock);
!         return ;
!     }   
  
!     tsrp = *list;
!     while (--picked >= 0)
!     {
!         lastTsrp = tsrp;
!         tsrp = tsrp->next;
!     }
!     lastTsrp->next = tsrp->next;  /* delete random element from list*/
!     tsrp->next     = *list; /* insert element at the beginning of list */
!     *list          = tsrp;
      lock_ReleaseWrite(&cm_serverLock);
! }       
  
  /* call cm_FreeServer while holding a write lock on cm_serverLock */
  void cm_FreeServer(cm_server_t* serverp)
***************
*** 447,453 ****
  {
      cm_serverRef_t  **current = list;
      cm_serverRef_t  **nextp = 0;
! 	cm_serverRef_t  * next = 0;
  
      lock_ObtainWrite(&cm_serverLock);
  
--- 450,456 ----
  {
      cm_serverRef_t  **current = list;
      cm_serverRef_t  **nextp = 0;
!     cm_serverRef_t  * next = 0;
  
      lock_ObtainWrite(&cm_serverLock);
  
***************
*** 455,461 ****
      {
          nextp = &(*current)->next;
          if (--((*current)->refCount) == 0) {
! 			next = *nextp;
              cm_FreeServer((*current)->server);
              free(*current);
              *current = next;
--- 458,464 ----
      {
          nextp = &(*current)->next;
          if (--((*current)->refCount) == 0) {
!             next = *nextp;
              cm_FreeServer((*current)->server);
              free(*current);
              *current = next;
Index: openafs/src/WINNT/afsd/cm_server.h
diff -c openafs/src/WINNT/afsd/cm_server.h:1.5.2.1 openafs/src/WINNT/afsd/cm_server.h:1.5.2.2
*** openafs/src/WINNT/afsd/cm_server.h:1.5.2.1	Tue Aug 17 00:28:39 2004
--- openafs/src/WINNT/afsd/cm_server.h	Mon Oct 18 00:09:26 2004
***************
*** 27,33 ****
  	struct cm_conn *connsp;			/* locked by cm_connLock */
      long flags;				/* by mx */
      struct cm_cell *cellp;			/* cell containing this server */
! 	int refCount;				/* locked by cm_serverLock */
      osi_mutex_t mx;
  	unsigned short ipRank;			/* server priority */
  } cm_server_t;
--- 27,33 ----
  	struct cm_conn *connsp;			/* locked by cm_connLock */
      long flags;				/* by mx */
      struct cm_cell *cellp;			/* cell containing this server */
! 	unsigned long refCount;				/* locked by cm_serverLock */
      osi_mutex_t mx;
  	unsigned short ipRank;			/* server priority */
  } cm_server_t;
***************
*** 38,44 ****
  	struct cm_serverRef *next;      /* locked by cm_serverLock */
  	struct cm_server *server;       /* locked by cm_serverLock */
  	enum repstate status;           /* locked by cm_serverLock */
!     int refCount;                   /* locked by cm_serverLock */
  } cm_serverRef_t;
  
  /* types */
--- 38,44 ----
  	struct cm_serverRef *next;      /* locked by cm_serverLock */
  	struct cm_server *server;       /* locked by cm_serverLock */
  	enum repstate status;           /* locked by cm_serverLock */
!     unsigned long refCount;                   /* locked by cm_serverLock */
  } cm_serverRef_t;
  
  /* types */
Index: openafs/src/WINNT/afsd/cm_user.h
diff -c openafs/src/WINNT/afsd/cm_user.h:1.2 openafs/src/WINNT/afsd/cm_user.h:1.2.20.1
*** openafs/src/WINNT/afsd/cm_user.h:1.2	Sat Nov  4 05:01:40 2000
--- openafs/src/WINNT/afsd/cm_user.h	Mon Oct 18 00:09:26 2004
***************
*** 39,45 ****
  #define CM_UCELLFLAG_BADTIX	4	/* tickets are bad or expired */
  
  typedef struct cm_user {
! 	int refCount;			/* ref count */
  	cm_ucell_t *cellInfop;		/* list of cell info */
          osi_mutex_t mx;			/* mutex */
          int vcRefs;			/* count of references from virtual circuits */
--- 39,45 ----
  #define CM_UCELLFLAG_BADTIX	4	/* tickets are bad or expired */
  
  typedef struct cm_user {
! 	unsigned long refCount;			/* ref count */
  	cm_ucell_t *cellInfop;		/* list of cell info */
          osi_mutex_t mx;			/* mutex */
          int vcRefs;			/* count of references from virtual circuits */
Index: openafs/src/WINNT/afsd/cm_utils.c
diff -c openafs/src/WINNT/afsd/cm_utils.c:1.5 openafs/src/WINNT/afsd/cm_utils.c:1.5.14.1
*** openafs/src/WINNT/afsd/cm_utils.c:1.5	Sat Sep  8 00:31:22 2001
--- openafs/src/WINNT/afsd/cm_utils.c	Tue Sep 21 10:07:18 2004
***************
*** 30,70 ****
  
  long cm_MapRPCError(long error, cm_req_t *reqp)
  {
! 	if (error == 0) return 0;
! 
! 	/* If we had to stop retrying, report our saved error code. */
! 	if (reqp && error == CM_ERROR_TIMEDOUT) {
! 		if (reqp->accessError)
! 			return reqp->accessError;
! 		if (reqp->volumeError)
! 			return reqp->volumeError;
! 		if (reqp->rpcError)
! 			return reqp->rpcError;
! 		return error;
! 	}
  
! 	if (error < 0) error = CM_ERROR_TIMEDOUT;
! 	else if (error == 30) error = CM_ERROR_READONLY;
!         else if (error == 13) error = CM_ERROR_NOACCESS;
!         else if (error == 18) error = CM_ERROR_CROSSDEVLINK;
!         else if (error == 17) error = CM_ERROR_EXISTS;
! 	else if (error == 20) error = CM_ERROR_NOTDIR;
!         else if (error == 2) error = CM_ERROR_NOSUCHFILE;
! 	else if (error == 11		/* EAGAIN, most servers */
! 		 || error == 35)	/* EAGAIN, Digital UNIX */
! 			error = CM_ERROR_WOULDBLOCK;
! 	else if (error == VDISKFULL
! 		 || error == 28)        /* ENOSPC */ 
! 	                error = CM_ERROR_SPACE;
! 	else if (error == VOVERQUOTA
! 		 || error == 49         /* EDQUOT on Solaris */
! 		 || error == 88		/* EDQUOT on AIX */
! 		 || error == 69	        /* EDQUOT on Digital UNIX and HPUX */
! 		 || error == 122        /* EDQUOT on Linux */
! 		 || error == 1133)      /* EDQUOT on Irix  */
! 	                error = CM_ERROR_QUOTA;
!         else if (error == VNOVNODE) error = CM_ERROR_BADFD;
          return error;
  }
  
  long cm_MapRPCErrorRmdir(long error, cm_req_t *reqp)
--- 30,81 ----
  
  long cm_MapRPCError(long error, cm_req_t *reqp)
  {
!     if (error == 0) 
!         return 0;
  
!     /* If we had to stop retrying, report our saved error code. */
!     if (reqp && error == CM_ERROR_TIMEDOUT) {
!         if (reqp->accessError)
!             return reqp->accessError;
!         if (reqp->volumeError)
!             return reqp->volumeError;
!         if (reqp->rpcError)
!             return reqp->rpcError;
          return error;
+     }
+ 
+     if (error < 0) 
+         error = CM_ERROR_TIMEDOUT;
+     else if (error == 30) 
+         error = CM_ERROR_READONLY;
+     else if (error == 13) 
+         error = CM_ERROR_NOACCESS;
+     else if (error == 18) 
+         error = CM_ERROR_CROSSDEVLINK;
+     else if (error == 17) 
+         error = CM_ERROR_EXISTS;
+     else if (error == 20) 
+         error = CM_ERROR_NOTDIR;
+     else if (error == 2) 
+         error = CM_ERROR_NOSUCHFILE;
+     else if (error == 11		/* EAGAIN, most servers */
+              || error == 35)	/* EAGAIN, Digital UNIX */
+         error = CM_ERROR_WOULDBLOCK;
+     else if (error == VDISKFULL
+               || error == 28)        /* ENOSPC */ 
+         error = CM_ERROR_SPACE;
+     else if (error == VOVERQUOTA
+               || error == 49         /* EDQUOT on Solaris */
+               || error == 88		/* EDQUOT on AIX */
+               || error == 69	        /* EDQUOT on Digital UNIX and HPUX */
+               || error == 122        /* EDQUOT on Linux */
+               || error == 1133)      /* EDQUOT on Irix  */
+         error = CM_ERROR_QUOTA;
+     else if (error == VNOVNODE) 
+         error = CM_ERROR_BADFD;
+     else if (error == 21)
+         return CM_ERROR_ISDIR;
+     return error;
  }
  
  long cm_MapRPCErrorRmdir(long error, cm_req_t *reqp)
Index: openafs/src/WINNT/afsd/cm_vnodeops.c
diff -c openafs/src/WINNT/afsd/cm_vnodeops.c:1.19.2.1 openafs/src/WINNT/afsd/cm_vnodeops.c:1.19.2.4
*** openafs/src/WINNT/afsd/cm_vnodeops.c:1.19.2.1	Tue Aug 17 00:28:39 2004
--- openafs/src/WINNT/afsd/cm_vnodeops.c	Mon Oct 18 00:09:26 2004
***************
*** 80,102 ****
   */
  int cm_stricmp(const char *str1, const char *str2)
  {
! 	char c1, c2;
  
! 	while (1) {
! 		if (*str1 == 0)
! 			if (*str2 == 0)
! 				return 0;
! 			else
! 				return -1;
! 		if (*str2 == 0)
! 			return 1;
! 		c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
! 		c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
! 		if (c1 < c2)
! 			return -1;
! 		if (c1 > c2)
! 			return 1;
! 	}
  }
  
  /* characters that are legal in an 8.3 name */
--- 80,102 ----
   */
  int cm_stricmp(const char *str1, const char *str2)
  {
!     char c1, c2;
  
!     while (1) {
!         if (*str1 == 0)
!             if (*str2 == 0)
!                 return 0;
!             else
!                 return -1;
!         if (*str2 == 0)
!             return 1;
!         c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
!         c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
!         if (c1 < c2)
!             return -1;
!         if (c1 > c2)
!             return 1;
!     }
  }
  
  /* characters that are legal in an 8.3 name */
***************
*** 129,181 ****
  /* return true iff component is a valid 8.3 name */
  int cm_Is8Dot3(char *namep)
  {
! 	int sawDot = 0;
! 	int sawUpper = 0, sawLower = 0;
!         unsigned char tc;
!         int charCount = 0;
!         
! 	/*
! 	 * can't have a leading dot;
! 	 * special case for . and ..
! 	 */
! 	if (namep[0] == '.') {
! 		if (namep[1] == 0)
! 			return 1;
! 		if (namep[1] == '.' && namep[2] == 0)
! 			return 1;
! 		return 0;
! 	}
!         while (tc = *namep++) {
! 		if (tc == '.') {
! 			/* saw another dot */
!                         if (sawDot) return 0;	/* second dot */
!                         sawDot = 1;
! 			charCount = 0;
!                         continue;
!                 }
! 		if (cm_LegalChars[tc] == 0)
! 			return 0;
! 		if (tc >= 'A' && tc <= 'Z')
! 			sawUpper = 1;
! 		else if (tc >= 'a' && tc <= 'z')
! 			sawLower = 1;
!                 charCount++;
!                 if (!sawDot && charCount > 8)
! 			/* more than 8 chars in name */
! 			return 0;
!                 if (sawDot && charCount > 3)
! 			/* more than 3 chars in extension */
! 			return 0;
!         }
! /*
!  * Used to check that all characters were the same case.
!  * This doesn't help 16-bit apps, and meanwhile it causes the
!  * MS-DOS Command Prompt to misbehave; see Sybase defect 10709.
!  *
! 	if (sawUpper && sawLower)
! 		return 0;
!  */
!         return 1;
  }
  
  /*
--- 129,181 ----
  /* return true iff component is a valid 8.3 name */
  int cm_Is8Dot3(char *namep)
  {
!     int sawDot = 0;
!     int sawUpper = 0, sawLower = 0;
!     unsigned char tc;
!     int charCount = 0;
!         
!     /*
!      * can't have a leading dot;
!      * special case for . and ..
!      */
!     if (namep[0] == '.') {
!         if (namep[1] == 0)
!             return 1;
!         if (namep[1] == '.' && namep[2] == 0)
!             return 1;
!         return 0;
!     }
!     while (tc = *namep++) {
!         if (tc == '.') {
!             /* saw another dot */
!             if (sawDot) return 0;	/* second dot */
!             sawDot = 1;
!             charCount = 0;
!             continue;
!         }
!         if (cm_LegalChars[tc] == 0)
!             return 0;
!         if (tc >= 'A' && tc <= 'Z')
!             sawUpper = 1;
!         else if (tc >= 'a' && tc <= 'z')
!             sawLower = 1;
!         charCount++;
!         if (!sawDot && charCount > 8)
!             /* more than 8 chars in name */
!             return 0;
!         if (sawDot && charCount > 3)
!             /* more than 3 chars in extension */
!             return 0;
!     }
!     /*
!      * Used to check that all characters were the same case.
!      * This doesn't help 16-bit apps, and meanwhile it causes the
!      * MS-DOS Command Prompt to misbehave; see Sybase defect 10709.
!      *
!      if (sawUpper && sawLower)
!          return 0;
!      */
!     return 1;
  }
  
  /*
***************
*** 191,323 ****
  
  void cm_Gen8Dot3Name(cm_dirEntry_t *dep, char *shortName, char **shortNameEndp)
  {
! 	char number[12];
! 	int i, nsize = 0;
! 	int vnode = ntohl(dep->fid.vnode);
! 	char *lastDot;
! 	int validExtension = 0;
! 	char tc, *temp, *name;
! 
! 	/* Unparse the file's vnode number to get a "uniquifier" */
! 	do {
! 		number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
! 		nsize++;
! 		vnode /= cm_8Dot3MapSize;
! 	} while (vnode);
! 
! 	/*
! 	 * Look for valid extension.  There has to be a dot, and
! 	 * at least one of the characters following has to be legal.
! 	 */
! 	lastDot = strrchr(dep->name, '.');
! 	if (lastDot) {
! 		temp = lastDot; temp++;
! 		while (tc = *temp++)
! 			if (cm_LegalChars[tc])
! 				break;
! 		if (tc)
! 			validExtension = 1;
! 	}
! 
! 	/* Copy name characters */
! 	name = dep->name;
! 	for (i = 0, name = dep->name;
! 	     i < (7 - nsize) && name != lastDot; ) {
! 		tc = *name++;
! 
! 		if (tc == 0)
! 			break;
! 		if (!cm_LegalChars[tc])
! 			continue;
! 		i++;
! 		*shortName++ = toupper(tc);
! 	}
! 
! 	/* tilde */
! 	*shortName++ = '~';
! 
! 	/* Copy uniquifier characters */
! 	memcpy(shortName, number, nsize);
! 	shortName += nsize;
! 
! 	if (validExtension) {
! 		/* Copy extension characters */
! 		*shortName++ = *lastDot++;	/* copy dot */
! 		for (i = 0, tc = *lastDot++;
! 		     i < 3 && tc;
! 		     tc = *lastDot++) {
! 			if (cm_LegalChars[tc]) {
! 				i++;
! 				*shortName++ = toupper(tc);
! 			}
! 		}
! 	}
  
! 	/* Trailing null */
! 	*shortName = 0;
  
! 	if (shortNameEndp)
! 		*shortNameEndp = shortName;
! }
  
  /* return success if we can open this file in this mode */
  long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
! 	cm_req_t *reqp)
  {
! 	long rights;
!         long code;
! 
! 	rights = 0;
! 	if (openMode != 1) rights |= PRSFS_READ;
!         if (openMode == 1 || openMode == 2 || trunc) rights |= PRSFS_WRITE;
!         
! 	lock_ObtainMutex(&scp->mx);
  
!         code = cm_SyncOp(scp, NULL, userp, reqp, rights,
! 			 CM_SCACHESYNC_GETSTATUS
!         		 | CM_SCACHESYNC_NEEDCALLBACK);
! 	lock_ReleaseMutex(&scp->mx);
  
! 	return code;
  }
  
  /* return success if we can open this file in this mode */
  long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
! 	unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp)
  {
! 	long rights;
!         long code;
! 
! 	/* Always allow delete; the RPC will tell us if it's OK */
! 	if (desiredAccess == DELETE)
! 		return 0;
  
! 	rights = 0;
  
! 	if (desiredAccess & AFS_ACCESS_READ)
! 		rights |= PRSFS_READ;
  
!         if ((desiredAccess & AFS_ACCESS_WRITE)
! 	    || createDisp == 4)
! 		rights |= PRSFS_WRITE;
!         
! 	lock_ObtainMutex(&scp->mx);
  
!         code = cm_SyncOp(scp, NULL, userp, reqp, rights,
! 			 CM_SCACHESYNC_GETSTATUS
!         		 | CM_SCACHESYNC_NEEDCALLBACK);
! 	lock_ReleaseMutex(&scp->mx);
! 
!         /*
!          * If the open will fail because the volume is readonly, then we will
!          * return an access denied error instead.  This is to help brain-dead
!          * apps run correctly on replicated volumes.
!          * See defect 10007 for more information.
!          */
!         if (code == CM_ERROR_READONLY)
!                 code = CM_ERROR_NOACCESS;
  
! 	return code;
  }
  
  /*
--- 191,323 ----
  
  void cm_Gen8Dot3Name(cm_dirEntry_t *dep, char *shortName, char **shortNameEndp)
  {
!     char number[12];
!     int i, nsize = 0;
!     int vnode = ntohl(dep->fid.vnode);
!     char *lastDot;
!     int validExtension = 0;
!     char tc, *temp, *name;
! 
!     /* Unparse the file's vnode number to get a "uniquifier" */
!     do {
!         number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
!         nsize++;
!         vnode /= cm_8Dot3MapSize;
!     } while (vnode);
! 
!     /*
!      * Look for valid extension.  There has to be a dot, and
!      * at least one of the characters following has to be legal.
!      */
!     lastDot = strrchr(dep->name, '.');
!     if (lastDot) {
!         temp = lastDot; temp++;
!         while (tc = *temp++)
!             if (cm_LegalChars[tc])
!                 break;
!         if (tc)
!             validExtension = 1;
!     }       
! 
!     /* Copy name characters */
!     name = dep->name;
!     for (i = 0, name = dep->name;
!           i < (7 - nsize) && name != lastDot; ) {
!         tc = *name++;
! 
!         if (tc == 0)
!             break;
!         if (!cm_LegalChars[tc])
!             continue;
!         i++;
!         *shortName++ = toupper(tc);
!     }
  
!     /* tilde */
!     *shortName++ = '~';
  
!     /* Copy uniquifier characters */
!     memcpy(shortName, number, nsize);
!     shortName += nsize;
! 
!     if (validExtension) {
!         /* Copy extension characters */
!         *shortName++ = *lastDot++;	/* copy dot */
!         for (i = 0, tc = *lastDot++;
!               i < 3 && tc;
!               tc = *lastDot++) {
!             if (cm_LegalChars[tc]) {
!                 i++;
!                 *shortName++ = toupper(tc);
!             }
!         }
!     }
! 
!     /* Trailing null */
!     *shortName = 0;
! 
!     if (shortNameEndp)
!         *shortNameEndp = shortName;
! }       
  
  /* return success if we can open this file in this mode */
  long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
!                   cm_req_t *reqp)
  {
!     long rights;
!     long code;
  
!     rights = 0;
!     if (openMode != 1) rights |= PRSFS_READ;
!     if (openMode == 1 || openMode == 2 || trunc) rights |= PRSFS_WRITE;
!         
!     lock_ObtainMutex(&scp->mx);
! 
!     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
!                       CM_SCACHESYNC_GETSTATUS
!                       | CM_SCACHESYNC_NEEDCALLBACK);
!     lock_ReleaseMutex(&scp->mx);
  
!     return code;
  }
  
  /* return success if we can open this file in this mode */
  long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
!                     unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp)
  {
!     long rights;
!     long code;
  
!     /* Always allow delete; the RPC will tell us if it's OK */
!     if (desiredAccess == DELETE)
!         return 0;
  
!     rights = 0;
  
!     if (desiredAccess & AFS_ACCESS_READ)
!         rights |= PRSFS_READ;
  
!     if ((desiredAccess & AFS_ACCESS_WRITE)
!          || createDisp == 4)
!         rights |= PRSFS_WRITE;
! 
!     lock_ObtainMutex(&scp->mx);
! 
!     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
!                       CM_SCACHESYNC_GETSTATUS
!                       | CM_SCACHESYNC_NEEDCALLBACK);
!     lock_ReleaseMutex(&scp->mx);
! 
!     /*
!      * If the open will fail because the volume is readonly, then we will
!      * return an access denied error instead.  This is to help brain-dead
!      * apps run correctly on replicated volumes.
!      * See defect 10007 for more information.
!      */
!     if (code == CM_ERROR_READONLY)
!         code = CM_ERROR_NOACCESS;
  
!     return code;
  }
  
  /*
***************
*** 336,427 ****
  long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
  	cm_req_t *reqp)
  {
! 	long code;
! 	osi_hyper_t thyper;
! 	cm_buf_t *bufferp;
! 	cm_dirEntry_t *dep;
! 	unsigned short *hashTable;
! 	unsigned int i, idx;
! 	int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
! 
! 	/* First check permissions */
! 	lock_ObtainMutex(&dscp->mx);
!         code = cm_SyncOp(dscp, NULL, userp, reqp, PRSFS_DELETE,
! 			 CM_SCACHESYNC_GETSTATUS
!         		 | CM_SCACHESYNC_NEEDCALLBACK);
! 	lock_ReleaseMutex(&dscp->mx);
! 	if (code)
! 		return code;
! 
! 	/* If deleting directory, must be empty */
! 
! 	if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
! 		return code;
! 
! 	thyper.HighPart = 0; thyper.LowPart = 0;
! 	lock_ObtainRead(&scp->bufCreateLock);
! 	code = buf_Get(scp, &thyper, &bufferp);
! 	lock_ReleaseRead(&scp->bufCreateLock);
! 	if (code)
! 		return code;
! 
! 	lock_ObtainMutex(&bufferp->mx);
! 	lock_ObtainMutex(&scp->mx);
! 	while (1) {
! 		code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
! 				 CM_SCACHESYNC_NEEDCALLBACK
! 				 | CM_SCACHESYNC_READ
! 				 | CM_SCACHESYNC_BUFLOCKED);
! 		if (code)
! 			break;
! 
! 		if (cm_HaveBuffer(scp, bufferp, 1))
! 			break;
! 
! 		/* otherwise, load the buffer and try again */
! 		lock_ReleaseMutex(&bufferp->mx);
! 		code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
! 		lock_ReleaseMutex(&scp->mx);
! 		lock_ObtainMutex(&bufferp->mx);
! 		lock_ObtainMutex(&scp->mx);
! 		if (code)
! 			break;
! 	}
! 
! 	/* We try to determine emptiness without looking beyond the first page,
! 	 * and without assuming "." and ".." are present and are on the first
! 	 * page (though these assumptions might, after all, be reasonable).
! 	 */
! 	hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
! 	for (i=0; i<128; i++) {
! 		idx = ntohs(hashTable[i]);
! 		while (idx) {
! 			if (idx >= 64) {
! 				BeyondPage = 1;
! 				break;
! 			}
! 			dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
! 			if (strcmp(dep->name, ".") == 0)
! 				HaveDot = 1;
! 			else if (strcmp(dep->name, "..") == 0)
! 				HaveDotDot = 1;
! 			else {
! 				code = CM_ERROR_NOTEMPTY;
! 				goto done;
! 			}
! 			idx = ntohs(dep->next);
! 		}
! 	}
! 	if (BeyondPage && HaveDot && HaveDotDot)
! 		code = CM_ERROR_NOTEMPTY;
! 	else
! 		code = 0;
! done:
! 	lock_ReleaseMutex(&bufferp->mx);
! 	buf_Release(bufferp);
! 	lock_ReleaseMutex(&scp->mx);
! 	return code;
! }
  
  /*
   * Iterate through all entries in a directory.
--- 336,427 ----
  long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
  	cm_req_t *reqp)
  {
!     long code;
!     osi_hyper_t thyper;
!     cm_buf_t *bufferp;
!     cm_dirEntry_t *dep;
!     unsigned short *hashTable;
!     unsigned int i, idx;
!     int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
! 
!     /* First check permissions */
!     lock_ObtainMutex(&dscp->mx);
!     code = cm_SyncOp(dscp, NULL, userp, reqp, PRSFS_DELETE,
!                       CM_SCACHESYNC_GETSTATUS
!                       | CM_SCACHESYNC_NEEDCALLBACK);
!     lock_ReleaseMutex(&dscp->mx);
!     if (code)
!         return code;
! 
!     /* If deleting directory, must be empty */
! 
!     if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
!         return code;
! 
!     thyper.HighPart = 0; thyper.LowPart = 0;
!     lock_ObtainRead(&scp->bufCreateLock);
!     code = buf_Get(scp, &thyper, &bufferp);
!     lock_ReleaseRead(&scp->bufCreateLock);
!     if (code)
!         return code;
! 
!     lock_ObtainMutex(&bufferp->mx);
!     lock_ObtainMutex(&scp->mx);
!     while (1) {
!         code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
!                           CM_SCACHESYNC_NEEDCALLBACK
!                           | CM_SCACHESYNC_READ
!                           | CM_SCACHESYNC_BUFLOCKED);
!         if (code)
!             break;
! 
!         if (cm_HaveBuffer(scp, bufferp, 1))
!             break;
! 
!         /* otherwise, load the buffer and try again */
!         lock_ReleaseMutex(&bufferp->mx);
!         code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
!         lock_ReleaseMutex(&scp->mx);
!         lock_ObtainMutex(&bufferp->mx);
!         lock_ObtainMutex(&scp->mx);
!         if (code)
!             break;
!     }
! 
!     /* We try to determine emptiness without looking beyond the first page,
!      * and without assuming "." and ".." are present and are on the first
!      * page (though these assumptions might, after all, be reasonable).
!      */
!     hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
!     for (i=0; i<128; i++) {
!         idx = ntohs(hashTable[i]);
!         while (idx) {
!             if (idx >= 64) {
!                 BeyondPage = 1;
!                 break;
!             }
!             dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
!             if (strcmp(dep->name, ".") == 0)
!                 HaveDot = 1;
!             else if (strcmp(dep->name, "..") == 0)
!                 HaveDotDot = 1;
!             else {
!                 code = CM_ERROR_NOTEMPTY;
!                 goto done;
!             }
!             idx = ntohs(dep->next);
!         }
!     }
!     if (BeyondPage && HaveDot && HaveDotDot)
!         code = CM_ERROR_NOTEMPTY;
!     else
!         code = 0;
!   done:   
!     lock_ReleaseMutex(&bufferp->mx);
!     buf_Release(bufferp);
!     lock_ReleaseMutex(&scp->mx);
!     return code;
! }       
  
  /*
   * Iterate through all entries in a directory.
***************
*** 429,436 ****
   * directory vnode is not.
   */
  long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
! 	osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
! 	cm_scache_t **retscp)
  {
      char *tp;
      long code;
--- 429,436 ----
   * directory vnode is not.
   */
  long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
!                   osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
!                   cm_scache_t **retscp)
  {
      char *tp;
      long code;
***************
*** 443,516 ****
      osi_hyper_t thyper;
      long entryInDir;
      long entryInBuffer;
! 	cm_pageHeader_t *pageHeaderp;
      int slotInPage;
      long nextEntryCookie;
      int numDirChunks;	/* # of 32 byte dir chunks in this entry */
          
      /* get the directory size */
! 	lock_ObtainMutex(&scp->mx);
      code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
!                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
! 	if (code) {
! 		lock_ReleaseMutex(&scp->mx);
          return code;
      }
          
      if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
! 		lock_ReleaseMutex(&scp->mx);
! 		return CM_ERROR_NOTDIR;
!     }
  
! 	if (retscp) 			/* if this is a lookup call */
! 	{
! 		cm_lookupSearch_t*	sp = parmp;
          int casefold = sp->caseFold;
  
          sp->caseFold = 0; /* we have a strong preference for exact matches */
! 		if ( *retscp = cm_dnlcLookup(scp, sp))	/* dnlc hit */
! 		{
              sp->caseFold = casefold;
! 			lock_ReleaseMutex(&scp->mx);
! 			return 0;
! 		}
  
          sp->caseFold = casefold;
! 	}	
  
! 	/*
! 	 * XXX We only get the length once.  It might change when we drop the
! 	 * lock.
! 	 */
      dirLength = scp->length;
  
! 	lock_ReleaseMutex(&scp->mx);
  
      bufferp = NULL;
      bufferOffset.LowPart = bufferOffset.HighPart = 0;
! 	if (startOffsetp)
          curOffset = *startOffsetp;
! 	else {
          curOffset.HighPart = 0;
          curOffset.LowPart = 0;
! 	}   
  
      while (1) {
! 		/* make sure that curOffset.LowPart doesn't point to the first
           * 32 bytes in the 2nd through last dir page, and that it
! 		 * doesn't point at the first 13 32-byte chunks in the first
! 		 * dir page, since those are dir and page headers, and don't
! 		 * contain useful information.
           */
! 		temp = curOffset.LowPart & (2048-1);
          if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
              /* we're in the first page */
              if (temp < 13*32) temp = 13*32;
- 		}
- 		else {
- 			/* we're in a later dir page */
-             if (temp < 32) temp = 32;
          }
  		
          /* make sure the low order 5 bits are zero */
          temp &= ~(32-1);
--- 443,516 ----
      osi_hyper_t thyper;
      long entryInDir;
      long entryInBuffer;
!     cm_pageHeader_t *pageHeaderp;
      int slotInPage;
      long nextEntryCookie;
      int numDirChunks;	/* # of 32 byte dir chunks in this entry */
          
      /* get the directory size */
!     lock_ObtainMutex(&scp->mx);
      code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
!                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
!     if (code) {
!         lock_ReleaseMutex(&scp->mx);
          return code;
      }
          
      if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
!         lock_ReleaseMutex(&scp->mx);
!         return CM_ERROR_NOTDIR;
!     }   
  
!     if (retscp) 			/* if this is a lookup call */
!     {
!         cm_lookupSearch_t*	sp = parmp;
          int casefold = sp->caseFold;
  
          sp->caseFold = 0; /* we have a strong preference for exact matches */
!         if ( *retscp = cm_dnlcLookup(scp, sp))	/* dnlc hit */
!         {
              sp->caseFold = casefold;
!             lock_ReleaseMutex(&scp->mx);
!             return 0;
!         }
  
          sp->caseFold = casefold;
!     }	
  
!     /*
!      * XXX We only get the length once.  It might change when we drop the
!      * lock.
!      */
      dirLength = scp->length;
  
!     lock_ReleaseMutex(&scp->mx);
  
      bufferp = NULL;
      bufferOffset.LowPart = bufferOffset.HighPart = 0;
!     if (startOffsetp)
          curOffset = *startOffsetp;
!     else {
          curOffset.HighPart = 0;
          curOffset.LowPart = 0;
!     }   
  
      while (1) {
!         /* make sure that curOffset.LowPart doesn't point to the first
           * 32 bytes in the 2nd through last dir page, and that it
!          * doesn't point at the first 13 32-byte chunks in the first
!          * dir page, since those are dir and page headers, and don't
!          * contain useful information.
           */
!         temp = curOffset.LowPart & (2048-1);
          if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
              /* we're in the first page */
              if (temp < 13*32) temp = 13*32;
          }
+         else {
+             /* we're in a later dir page */
+             if (temp < 32) temp = 32;
+         }       
  		
          /* make sure the low order 5 bits are zero */
          temp &= ~(32-1);
***************
*** 521,1007 ****
  
          /* check if we've passed the dir's EOF */
          if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
! 			break;
                  
          /* see if we can use the bufferp we have now; compute in which
           * page the current offset would be, and check whether that's
! 		 * the offset of the buffer we have.  If not, get the buffer.
! 		 */
          thyper.HighPart = curOffset.HighPart;
          thyper.LowPart = curOffset.LowPart & ~(buf_bufferSize-1);
          if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
! 			/* wrong buffer */
              if (bufferp) {
! 				lock_ReleaseMutex(&bufferp->mx);
                  buf_Release(bufferp);
                  bufferp = NULL;
! 			}
  
! 			lock_ObtainRead(&scp->bufCreateLock);
              code = buf_Get(scp, &thyper, &bufferp);
! 			lock_ReleaseRead(&scp->bufCreateLock);
  
! 			lock_ObtainMutex(&bufferp->mx);
!             if (code) break;
              bufferOffset = thyper;
  
              /* now get the data in the cache */
              while (1) {
                  lock_ObtainMutex(&scp->mx);
! 				code = cm_SyncOp(scp, bufferp, userp, reqp,
!                                  PRSFS_LOOKUP,
!                                  CM_SCACHESYNC_NEEDCALLBACK
!                                  | CM_SCACHESYNC_READ
!                                  | CM_SCACHESYNC_BUFLOCKED);
! 				if (code) {
! 					lock_ReleaseMutex(&scp->mx);
! 					break;
! 				}
                                  
                  if (cm_HaveBuffer(scp, bufferp, 1)) {
! 					lock_ReleaseMutex(&scp->mx);
! 					break;
! 				}
!                                 
                  /* otherwise, load the buffer and try again */
                  lock_ReleaseMutex(&bufferp->mx);
                  code = cm_GetBuffer(scp, bufferp, NULL, userp,
                                      reqp);
                  lock_ReleaseMutex(&scp->mx);
                  lock_ObtainMutex(&bufferp->mx);
!                 if (code) break;
              }
              if (code) {
! 				lock_ReleaseMutex(&bufferp->mx);
! 				buf_Release(bufferp);
                  bufferp = NULL;
                  break;
! 			}
          }	/* if (wrong buffer) ... */
                  
          /* now we have the buffer containing the entry we're interested
           * in; copy it out if it represents a non-deleted entry.
           */
! 		entryInDir = curOffset.LowPart & (2048-1);
          entryInBuffer = curOffset.LowPart & (buf_bufferSize - 1);
  
! 		/* page header will help tell us which entries are free.  Page
! 		 * header can change more often than once per buffer, since
! 		 * AFS 3 dir page size may be less than (but not more than) a
! 		 * buffer package buffer.
           */
! 		/* only look intra-buffer */
! 		temp = curOffset.LowPart & (buf_bufferSize - 1);
          temp &= ~(2048 - 1);	/* turn off intra-page bits */
! 		pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
  
! 		/* now determine which entry we're looking at in the page.  If
! 		 * it is free (there's a free bitmap at the start of the dir),
! 		 * we should skip these 32 bytes.
           */
          slotInPage = (entryInDir & 0x7e0) >> 5;
          if (!(pageHeaderp->freeBitmap[slotInPage>>3]
                 & (1 << (slotInPage & 0x7)))) {
! 			/* this entry is free */
              numDirChunks = 1;	/* only skip this guy */
              goto nextEntry;
          }
  
! 		tp = bufferp->datap + entryInBuffer;
          dep = (cm_dirEntry_t *) tp;	/* now points to AFS3 dir entry */
  
          /* while we're here, compute the next entry's location, too,
! 		 * since we'll need it when writing out the cookie into the
! 		 * dir listing stream.
           */
! 		numDirChunks = cm_NameEntries(dep->name, NULL);
  		
          /* compute the offset of the cookie representing the next entry */
          nextEntryCookie = curOffset.LowPart
! 			+ (CM_DIR_CHUNKSIZE * numDirChunks);
  
          if (dep->fid.vnode != 0) {
! 			/* this is one of the entries to use: it is not deleted */
! 			code = (*funcp)(scp, dep, parmp, &curOffset);
!             if (code) break;
! 		}	/* if we're including this name */
                  
        nextEntry:
          /* and adjust curOffset to be where the new cookie is */
! 		thyper.HighPart = 0;
          thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
          curOffset = LargeIntegerAdd(thyper, curOffset);
      }		/* while copying data for dir listing */
  
! 	/* release the mutex */
      if (bufferp) {
! 		lock_ReleaseMutex(&bufferp->mx);
          buf_Release(bufferp);
! 	}
      return code;
  }
  
  int cm_NoneUpper(char *s)
  {
! 	char c;
! 	while (c = *s++)
! 		if (c >= 'A' && c <= 'Z')
! 			return 0;
! 	return 1;
  }
  
  int cm_NoneLower(char *s)
  {
! 	char c;
! 	while (c = *s++)
! 		if (c >= 'a' && c <= 'z')
! 			return 0;
! 	return 1;
  }
  
  long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
! 	osi_hyper_t *offp)
  {
! 	cm_lookupSearch_t *sp;
      int match;
! 	char shortName[13];
! 	char *matchName;
!         
      sp = (cm_lookupSearch_t *) rockp;
  
! 	matchName = dep->name;
! 	if (sp->caseFold)
          match = cm_stricmp(matchName, sp->searchNamep);
! 	else
! 		match = strcmp(matchName, sp->searchNamep);
  
! 	if (match != 0
! 	    && sp->hasTilde
! 	    && !cm_Is8Dot3(dep->name)) {
! 		matchName = shortName;
! 		cm_Gen8Dot3Name(dep, shortName, NULL);
! 		if (sp->caseFold)
! 			match = cm_stricmp(matchName, sp->searchNamep);
! 		else
! 			match = strcmp(matchName, sp->searchNamep);
! 	}
! 
! 	if (match != 0)
! 		return 0;
! 
! 	sp->found = 1;
!     if(!sp->caseFold) sp->ExactFound = 1;
! 
! 	if (!sp->caseFold || matchName == shortName) {
! 		sp->fid.vnode = ntohl(dep->fid.vnode);
! 		sp->fid.unique = ntohl(dep->fid.unique);
          return CM_ERROR_STOPNOW;
      }
  
! 	/*
! 	 * If we get here, we are doing a case-insensitive search, and we
! 	 * have found a match.  Now we determine what kind of match it is:
! 	 * exact, lower-case, upper-case, or none of the above.  This is done
! 	 * in order to choose among matches, if there are more than one.
! 	 */
! 
! 	/* Exact matches are the best. */
! 	match = strcmp(matchName, sp->searchNamep);
! 	if (match == 0) {
          sp->ExactFound = 1;
! 		sp->fid.vnode = ntohl(dep->fid.vnode);
! 		sp->fid.unique = ntohl(dep->fid.unique);
          return CM_ERROR_STOPNOW;
      }
  
! 	/* Lower-case matches are next. */
! 	if (sp->LCfound)
! 		return 0;
! 	if (cm_NoneUpper(matchName)) {
! 		sp->LCfound = 1;
! 		goto inexact;
! 	}
! 
! 	/* Upper-case matches are next. */
! 	if (sp->UCfound)
! 		return 0;
! 	if (cm_NoneLower(matchName)) {
! 		sp->UCfound = 1;
! 		goto inexact;
! 	}
! 
! 	/* General matches are last. */
! 	if (sp->NCfound)
! 		return 0;
! 	sp->NCfound = 1;
! 
! inexact:
! 	sp->fid.vnode = ntohl(dep->fid.vnode);
! 	sp->fid.unique = ntohl(dep->fid.unique);
! 	return 0;
! }
  
  /* read the contents of a mount point into the appropriate string.
   * called with locked scp, and returns with locked scp.
   */
  long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
  {
! 	long code;
! 	cm_buf_t *bufp;
!         osi_hyper_t thyper;
!         int tlen;
  
! 	if (scp->mountPointStringp) return 0;
          
!         /* otherwise, we have to read it in */
!         lock_ReleaseMutex(&scp->mx);
  
! 	lock_ObtainRead(&scp->bufCreateLock);
!         thyper.LowPart = thyper.HighPart = 0;
! 	code = buf_Get(scp, &thyper, &bufp);
! 	lock_ReleaseRead(&scp->bufCreateLock);
  
! 	lock_ObtainMutex(&scp->mx);
          if (code) {
!                 return code;
          }
! 	while (1) {
! 	        code = cm_SyncOp(scp, bufp, userp, reqp, 0,
!                 	CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
! 	        if (code) {
! 	                goto done;
! 	        }
!                 
!                 if (cm_HaveBuffer(scp, bufp, 0)) break;
!                 
!                 /* otherwise load buffer */
!                 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
!                 if (code) {
! 			goto done;
! 		}
! 	}
!         /* locked, has callback, has valid data in buffer */
!         if ((tlen = scp->length.LowPart) > 1000) return CM_ERROR_TOOBIG;
!         if (tlen <= 0) {
!         	code = CM_ERROR_INVAL;
! 		goto done;
! 	}
!         
! 	/* someone else did the work while we were out */
!         if (scp->mountPointStringp) {
! 		code = 0;
!                 goto done;
          }
! 	
!         /* otherwise, copy out the link */
!         scp->mountPointStringp = malloc(tlen);
!         memcpy(scp->mountPointStringp, bufp->datap, tlen);
! 
! 	/* now make it null-terminated.  Note that the original contents of a
! 	 * link that is a mount point is "#volname." where "." is there just to
! 	 * be turned into a null.  That is, we can trash the last char of the
! 	 * link without damaging the vol name.  This is a stupid convention,
! 	 * but that's the protocol.
!          */
!         scp->mountPointStringp[tlen-1] = 0;
          code = 0;
  
! done:
! 	if (bufp) buf_Release(bufp);
!         return code;
  }
  
  /* called with a locked scp and chases the mount point, yielding outScpp.
   * scp remains locked, just for simplicity of describing the interface.
   */
  long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
! 	cm_req_t *reqp, cm_scache_t **outScpp)
  {
! 	char *cellNamep;
!         char *volNamep;
!         int tlen;
!         long code;
!         char *cp;
!         char *mpNamep;
! 	cm_volume_t *volp;
!         cm_cell_t *cellp;
!         char mtType;
!         cm_fid_t tfid;
! 	size_t vnLength;
! 	int type;
! 
! 	if (scp->mountRootFidp && scp->mountRootGen >= cm_mountRootGen) {
! 		tfid = *scp->mountRootFidp;
!                 lock_ReleaseMutex(&scp->mx);
! 		code = cm_GetSCache(&tfid, outScpp, userp, reqp);
!                 lock_ObtainMutex(&scp->mx);
!         	return code;
! 	}
! 
! 	/* parse the volume name */
! 	mpNamep = scp->mountPointStringp;
! 	osi_assert(mpNamep);
!         tlen = strlen(scp->mountPointStringp);
!         mtType = *scp->mountPointStringp;
!         cellNamep = malloc(tlen);
!         volNamep = malloc(tlen);
!         
! 	cp = strrchr(mpNamep, ':');
!         if (cp) {
! 		/* cellular mount point */
! 		memset(cellNamep, 0, tlen);
!                 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
!                 strcpy(volNamep, cp+1);
!                 /* now look up the cell */
!                 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
!         }
! 	else {
! 		/* normal mt pt */
!                 strcpy(volNamep, mpNamep+1);
!                 
!                 cellp = cm_FindCellByID(scp->fid.cell);
!         }
!         
! 	if (!cellp) {
! 		code = CM_ERROR_NOSUCHCELL;
! 		goto done;
! 	}
! 
! 	vnLength = strlen(volNamep);
! 	if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
! 		type = BACKVOL;
! 	else if (vnLength >= 10
! 		  && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
! 		type = ROVOL;
! 	else
! 		type = RWVOL;
! 
! 	/* check for backups within backups */
! 	if (type == BACKVOL
! 	    && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
! 		  == CM_SCACHEFLAG_RO) {
! 		code = CM_ERROR_NOSUCHVOLUME;
! 		goto done;
! 	}
  
!         /* now we need to get the volume */
          lock_ReleaseMutex(&scp->mx);
! 	code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
          lock_ObtainMutex(&scp->mx);
-         
-         if (code == 0) {
- 	        /* save the parent of the volume root for this is the 
- 		 * place where the volume is mounted and we must remember 
- 		 * this in the volume structure rather than just in the 
- 		 * scache entry lest the scache entry gets recycled 
- 		 * (defect 11489)
- 		 */
- 	        lock_ObtainMutex(&volp->mx);
- 	        if(volp->dotdotFidp == (cm_fid_t *) NULL) 
- 		        volp->dotdotFidp = (cm_fid_t *) malloc(sizeof(cm_fid_t));
- 		*(volp->dotdotFidp) = dscp->fid;
- 		lock_ReleaseMutex(&volp->mx);
- 
- 		if (scp->mountRootFidp == 0) {
- 			scp->mountRootFidp = malloc(sizeof(cm_fid_t));
-                 }
-                 scp->mountRootFidp->cell = cellp->cellID;
- 		/* if the mt pt is in a read-only volume (not just a
- 		 * backup), and if there is a read-only volume for the
- 		 * target, and if this is a type '#' mount point, use
- 		 * the read-only, otherwise use the one specified.
-                  */
-                 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
-                        	&& volp->roID != 0 && type == RWVOL)
- 			type = ROVOL;
- 		if (type == ROVOL)
-                        	scp->mountRootFidp->volume = volp->roID;
- 		else if (type == BACKVOL)
- 			scp->mountRootFidp->volume = volp->bkID;
-                 else
- 			scp->mountRootFidp->volume = volp->rwID;
- 
- 		/* the rest of the fid is a magic number */
-                 scp->mountRootFidp->vnode = 1;
-                 scp->mountRootFidp->unique = 1;
- 		scp->mountRootGen = cm_mountRootGen;
-                
- 		tfid = *scp->mountRootFidp;
-                 lock_ReleaseMutex(&scp->mx);
-                 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
-                 lock_ObtainMutex(&scp->mx);
-         }
- 
- done:
- 	free(cellNamep);
-         free(volNamep);
          return code;
! }
  
! int cm_ExpandSysName(char *inp, char *outp, long outSize)
! {
! 	char *tp;
!     int prefixCount;
  
!     tp = strrchr(inp, '@');
!     if (tp == NULL) return 0;		/* no @sys */
  
!     if (strcmp(tp, "@sys") != 0) return 0;	/* no @sys */
  
! 	/* caller just wants to know if this is a valid @sys type of name */
! 	if (outp == NULL) return 1;
  
! 	/* otherwise generate the properly expanded @sys name */
!     prefixCount = tp - inp;
  
!     strncpy(outp, inp, prefixCount);	/* copy out "a." from "a.@sys" */
!     outp[prefixCount] = 0;			/* null terminate the "a." */
!     strcat(outp, cm_sysName);		/* append i386_nt40 */
!     return 1;
! }   
  
! long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
! 	cm_req_t *reqp, cm_scache_t **outpScpp)
  {
! 	long code;
! 	int dnlcHit = 1;	/* did we hit in the dnlc? yes, we did */
      cm_scache_t *tscp = NULL;
      cm_scache_t *mountedScp;
      cm_lookupSearch_t rock;
!     char tname[256];
! 	int getroot;
  
! 	if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
           && strcmp(namep, "..") == 0) {
! 		if (dscp->dotdotFidp == (cm_fid_t *)NULL
               || dscp->dotdotFidp->volume == 0)
! 			return CM_ERROR_NOSUCHVOLUME;
! 		rock.fid = *dscp->dotdotFidp;
! 		goto haveFid;
! 	}
! 
! 	if (cm_ExpandSysName(namep, tname, sizeof(tname))) {
! 		namep = tname;
      }
! 	memset(&rock, 0, sizeof(rock));
      rock.fid.cell = dscp->fid.cell;
      rock.fid.volume = dscp->fid.volume;
      rock.searchNamep = namep;
      rock.caseFold = (flags & CM_FLAG_CASEFOLD);
! 	rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
  
! 	/* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
! 	code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
!                        (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
  
! 	/* code == 0 means we fell off the end of the dir, while stopnow means
       * that we stopped early, probably because we found the entry we're
! 	 * looking for.  Any other non-zero code is an error.
       */
      if (code && code != CM_ERROR_STOPNOW) 
          return code;
  
! 	getroot = (dscp==cm_rootSCachep) ;
      if (!rock.found) {
          if (!cm_freelanceEnabled || !getroot) {
              if (flags & CM_FLAG_CHECKPATH)
--- 521,988 ----
  
          /* check if we've passed the dir's EOF */
          if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
!             break;
                  
          /* see if we can use the bufferp we have now; compute in which
           * page the current offset would be, and check whether that's
!          * the offset of the buffer we have.  If not, get the buffer.
!          */
          thyper.HighPart = curOffset.HighPart;
          thyper.LowPart = curOffset.LowPart & ~(buf_bufferSize-1);
          if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
!             /* wrong buffer */
              if (bufferp) {
!                 lock_ReleaseMutex(&bufferp->mx);
                  buf_Release(bufferp);
                  bufferp = NULL;
!             }
  
!             lock_ObtainRead(&scp->bufCreateLock);
              code = buf_Get(scp, &thyper, &bufferp);
!             lock_ReleaseRead(&scp->bufCreateLock);
  
!             lock_ObtainMutex(&bufferp->mx);
!             if (code) 
!                 break;
              bufferOffset = thyper;
  
              /* now get the data in the cache */
              while (1) {
                  lock_ObtainMutex(&scp->mx);
!                 code = cm_SyncOp(scp, bufferp, userp, reqp,
!                                   PRSFS_LOOKUP,
!                                   CM_SCACHESYNC_NEEDCALLBACK
!                                   | CM_SCACHESYNC_READ
!                                   | CM_SCACHESYNC_BUFLOCKED);
!                 if (code) {
!                     lock_ReleaseMutex(&scp->mx);
!                     break;
!                 }
                                  
                  if (cm_HaveBuffer(scp, bufferp, 1)) {
!                     lock_ReleaseMutex(&scp->mx);
!                     break;
!                 }
! 
                  /* otherwise, load the buffer and try again */
                  lock_ReleaseMutex(&bufferp->mx);
                  code = cm_GetBuffer(scp, bufferp, NULL, userp,
                                      reqp);
                  lock_ReleaseMutex(&scp->mx);
                  lock_ObtainMutex(&bufferp->mx);
!                 if (code) 
!                     break;
              }
              if (code) {
!                 lock_ReleaseMutex(&bufferp->mx);
!                 buf_Release(bufferp);
                  bufferp = NULL;
                  break;
!             }
          }	/* if (wrong buffer) ... */
                  
          /* now we have the buffer containing the entry we're interested
           * in; copy it out if it represents a non-deleted entry.
           */
!         entryInDir = curOffset.LowPart & (2048-1);
          entryInBuffer = curOffset.LowPart & (buf_bufferSize - 1);
  
!         /* page header will help tell us which entries are free.  Page
!          * header can change more often than once per buffer, since
!          * AFS 3 dir page size may be less than (but not more than) a
!          * buffer package buffer.
           */
!         /* only look intra-buffer */
!         temp = curOffset.LowPart & (buf_bufferSize - 1);
          temp &= ~(2048 - 1);	/* turn off intra-page bits */
!         pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
  
!         /* now determine which entry we're looking at in the page.  If
!          * it is free (there's a free bitmap at the start of the dir),
!          * we should skip these 32 bytes.
           */
          slotInPage = (entryInDir & 0x7e0) >> 5;
          if (!(pageHeaderp->freeBitmap[slotInPage>>3]
                 & (1 << (slotInPage & 0x7)))) {
!             /* this entry is free */
              numDirChunks = 1;	/* only skip this guy */
              goto nextEntry;
          }
  
!         tp = bufferp->datap + entryInBuffer;
          dep = (cm_dirEntry_t *) tp;	/* now points to AFS3 dir entry */
  
          /* while we're here, compute the next entry's location, too,
!          * since we'll need it when writing out the cookie into the
!          * dir listing stream.
           */
!         numDirChunks = cm_NameEntries(dep->name, NULL);
  		
          /* compute the offset of the cookie representing the next entry */
          nextEntryCookie = curOffset.LowPart
!             + (CM_DIR_CHUNKSIZE * numDirChunks);
  
          if (dep->fid.vnode != 0) {
!             /* this is one of the entries to use: it is not deleted */
!             code = (*funcp)(scp, dep, parmp, &curOffset);
!             if (code) 
!                 break;
!         }	/* if we're including this name */
                  
        nextEntry:
          /* and adjust curOffset to be where the new cookie is */
!         thyper.HighPart = 0;
          thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
          curOffset = LargeIntegerAdd(thyper, curOffset);
      }		/* while copying data for dir listing */
  
!     /* release the mutex */
      if (bufferp) {
!         lock_ReleaseMutex(&bufferp->mx);
          buf_Release(bufferp);
!     }
      return code;
  }
  
  int cm_NoneUpper(char *s)
  {
!     char c;
!     while (c = *s++)
!         if (c >= 'A' && c <= 'Z')
!             return 0;
!     return 1;
  }
  
  int cm_NoneLower(char *s)
  {
!     char c;
!     while (c = *s++)
!         if (c >= 'a' && c <= 'z')
!             return 0;
!     return 1;
  }
  
  long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
!                           osi_hyper_t *offp)
  {
!     cm_lookupSearch_t *sp;
      int match;
!     char shortName[13];
!     char *matchName;
! 
      sp = (cm_lookupSearch_t *) rockp;
  
!     matchName = dep->name;
!     if (sp->caseFold)
          match = cm_stricmp(matchName, sp->searchNamep);
!     else
!         match = strcmp(matchName, sp->searchNamep);
  
!     if (match != 0
!          && sp->hasTilde
!          && !cm_Is8Dot3(dep->name)) {
!         matchName = shortName;
!         cm_Gen8Dot3Name(dep, shortName, NULL);
!         if (sp->caseFold)
!             match = cm_stricmp(matchName, sp->searchNamep);
!         else
!             match = strcmp(matchName, sp->searchNamep);
!     }       
! 
!     if (match != 0)
!         return 0;
! 
!     sp->found = 1;
!     if(!sp->caseFold) 
!         sp->ExactFound = 1;
! 
!     if (!sp->caseFold || matchName == shortName) {
!         sp->fid.vnode = ntohl(dep->fid.vnode);
!         sp->fid.unique = ntohl(dep->fid.unique);
          return CM_ERROR_STOPNOW;
      }
  
!     /*
!      * If we get here, we are doing a case-insensitive search, and we
!      * have found a match.  Now we determine what kind of match it is:
!      * exact, lower-case, upper-case, or none of the above.  This is done
!      * in order to choose among matches, if there are more than one.
!      */
! 
!     /* Exact matches are the best. */
!     match = strcmp(matchName, sp->searchNamep);
!     if (match == 0) {
          sp->ExactFound = 1;
!         sp->fid.vnode = ntohl(dep->fid.vnode);
!         sp->fid.unique = ntohl(dep->fid.unique);
          return CM_ERROR_STOPNOW;
      }
  
!     /* Lower-case matches are next. */
!     if (sp->LCfound)
!         return 0;
!     if (cm_NoneUpper(matchName)) {
!         sp->LCfound = 1;
!         goto inexact;
!     }
! 
!     /* Upper-case matches are next. */
!     if (sp->UCfound)
!         return 0;
!     if (cm_NoneLower(matchName)) {
!         sp->UCfound = 1;
!         goto inexact;
!     }
! 
!     /* General matches are last. */
!     if (sp->NCfound)
!         return 0;
!     sp->NCfound = 1;
! 
!   inexact:
!     sp->fid.vnode = ntohl(dep->fid.vnode);
!     sp->fid.unique = ntohl(dep->fid.unique);
!     return 0;
! }       
  
  /* read the contents of a mount point into the appropriate string.
   * called with locked scp, and returns with locked scp.
   */
  long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
  {
!     long code;
!     cm_buf_t *bufp;
!     osi_hyper_t thyper;
!     int tlen;
  
!     if (scp->mountPointStringp) 
!         return 0;
          
!     /* otherwise, we have to read it in */
!     lock_ReleaseMutex(&scp->mx);
  
!     lock_ObtainRead(&scp->bufCreateLock);
!     thyper.LowPart = thyper.HighPart = 0;
!     code = buf_Get(scp, &thyper, &bufp);
!     lock_ReleaseRead(&scp->bufCreateLock);
  
!     lock_ObtainMutex(&scp->mx);
!     if (code) {
!         return code;
!     }
!     while (1) {
!         code = cm_SyncOp(scp, bufp, userp, reqp, 0,
!                           CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
          if (code) {
!             goto done;
          }
! 
!         if (cm_HaveBuffer(scp, bufp, 0)) 
!             break;
! 
!         /* otherwise load buffer */
!         code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
!         if (code) {
!             goto done;
          }
!     }
!     /* locked, has callback, has valid data in buffer */
!     if ((tlen = scp->length.LowPart) > 1000) return CM_ERROR_TOOBIG;
!     if (tlen <= 0) {
!         code = CM_ERROR_INVAL;
!         goto done;
!     }
! 
!     /* someone else did the work while we were out */
!     if (scp->mountPointStringp) {
          code = 0;
+         goto done;
+     }
  
!     /* otherwise, copy out the link */
!     scp->mountPointStringp = malloc(tlen);
!     memcpy(scp->mountPointStringp, bufp->datap, tlen);
! 
!     /* now make it null-terminated.  Note that the original contents of a
!      * link that is a mount point is "#volname." where "." is there just to
!      * be turned into a null.  That is, we can trash the last char of the
!      * link without damaging the vol name.  This is a stupid convention,
!      * but that's the protocol.
!      */
!     scp->mountPointStringp[tlen-1] = 0;
!     code = 0;
! 
!   done:
!     if (bufp) 
!         buf_Release(bufp);
!     return code;
  }
  
  /* called with a locked scp and chases the mount point, yielding outScpp.
   * scp remains locked, just for simplicity of describing the interface.
   */
  long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
!                           cm_req_t *reqp, cm_scache_t **outScpp)
  {
!     char *cellNamep;
!     char *volNamep;
!     int tlen;
!     long code;
!     char *cp;
!     char *mpNamep;
!     cm_volume_t *volp;
!     cm_cell_t *cellp;
!     char mtType;
!     cm_fid_t tfid;
!     size_t vnLength;
!     int type;
  
!     if (scp->mountRootFidp && scp->mountRootGen >= cm_mountRootGen) {
!         tfid = *scp->mountRootFidp;
          lock_ReleaseMutex(&scp->mx);
!         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
          lock_ObtainMutex(&scp->mx);
          return code;
!     }
  
!     /* parse the volume name */
!     mpNamep = scp->mountPointStringp;
!     osi_assert(mpNamep);
!     tlen = strlen(scp->mountPointStringp);
!     mtType = *scp->mountPointStringp;
!     cellNamep = malloc(tlen);
!     volNamep = malloc(tlen);
! 
!     cp = strrchr(mpNamep, ':');
!     if (cp) {
!         /* cellular mount point */
!         memset(cellNamep, 0, tlen);
!         strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
!         strcpy(volNamep, cp+1);
!         /* now look up the cell */
!         cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
!     }
!     else {
!         /* normal mt pt */
!         strcpy(volNamep, mpNamep+1);
  
!         cellp = cm_FindCellByID(scp->fid.cell);
!     }
  
!     if (!cellp) {
!         code = CM_ERROR_NOSUCHCELL;
!         goto done;
!     }
  
!     vnLength = strlen(volNamep);
!     if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
!         type = BACKVOL;
!     else if (vnLength >= 10
!               && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
!         type = ROVOL;
!     else
!         type = RWVOL;
! 
!     /* check for backups within backups */
!     if (type == BACKVOL
!          && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
!          == CM_SCACHEFLAG_RO) {
!         code = CM_ERROR_NOSUCHVOLUME;
!         goto done;
!     }
  
!     /* now we need to get the volume */
!     lock_ReleaseMutex(&scp->mx);
!     code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
!     lock_ObtainMutex(&scp->mx);
!         
!     if (code == 0) {
!         /* save the parent of the volume root for this is the 
!          * place where the volume is mounted and we must remember 
!          * this in the volume structure rather than just in the 
!          * scache entry lest the scache entry gets recycled 
!          * (defect 11489)
!          */
!         lock_ObtainMutex(&volp->mx);
!         if(volp->dotdotFidp == (cm_fid_t *) NULL) 
!             volp->dotdotFidp = (cm_fid_t *) malloc(sizeof(cm_fid_t));
!         *(volp->dotdotFidp) = dscp->fid;
!         lock_ReleaseMutex(&volp->mx);
! 
!         if (scp->mountRootFidp == 0) {
!             scp->mountRootFidp = malloc(sizeof(cm_fid_t));
!         }
!         scp->mountRootFidp->cell = cellp->cellID;
!         /* if the mt pt is in a read-only volume (not just a
!          * backup), and if there is a read-only volume for the
!          * target, and if this is a type '#' mount point, use
!          * the read-only, otherwise use the one specified.
!          */
!         if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
!              && volp->roID != 0 && type == RWVOL)
!             type = ROVOL;
!         if (type == ROVOL)
!             scp->mountRootFidp->volume = volp->roID;
!         else if (type == BACKVOL)
!             scp->mountRootFidp->volume = volp->bkID;
!         else
!             scp->mountRootFidp->volume = volp->rwID;
! 
!         /* the rest of the fid is a magic number */
!         scp->mountRootFidp->vnode = 1;
!         scp->mountRootFidp->unique = 1;
!         scp->mountRootGen = cm_mountRootGen;
  
!         tfid = *scp->mountRootFidp;
!         lock_ReleaseMutex(&scp->mx);
!         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
!         lock_ObtainMutex(&scp->mx);
!     }
  
!   done:
!     free(cellNamep);
!     free(volNamep);
!     return code;
! }       
! 
! long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
!                        cm_req_t *reqp, cm_scache_t **outpScpp)
  {
!     long code;
!     int dnlcHit = 1;	/* did we hit in the dnlc? yes, we did */
      cm_scache_t *tscp = NULL;
      cm_scache_t *mountedScp;
      cm_lookupSearch_t rock;
!     int getroot;
  
!     if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
           && strcmp(namep, "..") == 0) {
!         if (dscp->dotdotFidp == (cm_fid_t *)NULL
               || dscp->dotdotFidp->volume == 0)
!             return CM_ERROR_NOSUCHVOLUME;
!         rock.fid = *dscp->dotdotFidp;
!         goto haveFid;
      }
! 
!     memset(&rock, 0, sizeof(rock));
      rock.fid.cell = dscp->fid.cell;
      rock.fid.volume = dscp->fid.volume;
      rock.searchNamep = namep;
      rock.caseFold = (flags & CM_FLAG_CASEFOLD);
!     rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
  
!     /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
!     code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
!                         (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
  
!     /* code == 0 means we fell off the end of the dir, while stopnow means
       * that we stopped early, probably because we found the entry we're
!      * looking for.  Any other non-zero code is an error.
       */
      if (code && code != CM_ERROR_STOPNOW) 
          return code;
  
!     getroot = (dscp==cm_rootSCachep) ;
      if (!rock.found) {
          if (!cm_freelanceEnabled || !getroot) {
              if (flags & CM_FLAG_CHECKPATH)
***************
*** 1010,1018 ****
                  return CM_ERROR_NOSUCHFILE;
          }
          else {  /* nonexistent dir on freelance root, so add it */
! 			osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s", 
!                      osi_LogSaveString(afsd_logp,namep));
! 			code = cm_FreelanceAddMount(namep, namep, "root.cell.", namep[0] == '.', &rock.fid);
              if (code < 0) {   /* add mount point failed, so give up */
                  if (flags & CM_FLAG_CHECKPATH)
                      return CM_ERROR_NOSUCHPATH;
--- 991,999 ----
                  return CM_ERROR_NOSUCHFILE;
          }
          else {  /* nonexistent dir on freelance root, so add it */
!             osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s", 
!                       osi_LogSaveString(afsd_logp,namep));
!             code = cm_FreelanceAddMount(namep, namep, "root.cell.", namep[0] == '.', &rock.fid);
              if (code < 0) {   /* add mount point failed, so give up */
                  if (flags & CM_FLAG_CHECKPATH)
                      return CM_ERROR_NOSUCHPATH;
***************
*** 1021,1129 ****
              }
              tscp = NULL;   /* to force call of cm_GetSCache */
          }
! 	}
  
! haveFid:       
! 	if ( !tscp )    /* we did not find it in the dnlc */
! 	{
! 		dnlcHit = 0;	
          code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
          if (code) 
              return code;
! 	}
      /* tscp is now held */
  
! 	lock_ObtainMutex(&tscp->mx);
! 	code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
                        CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
      if (code) { 
! 		lock_ReleaseMutex(&tscp->mx);
! 		cm_ReleaseSCache(tscp);
          return code;
! 	}
      /* tscp is now locked */
  
      if (!(flags & CM_FLAG_NOMOUNTCHASE)
! 	      && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
! 		/* mount points are funny: they have a volume name to mount
           * the root of.
           */
! 		code = cm_ReadMountPoint(tscp, userp, reqp);
          if (code == 0)
! 			code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
                                          &mountedScp);
! 		lock_ReleaseMutex(&tscp->mx);
! 		cm_ReleaseSCache(tscp);
! 		if (code) {
              return code;
          }
          tscp = mountedScp;
      }
! 	else {
! 		lock_ReleaseMutex(&tscp->mx);
! 	}
  
! 	/* copy back pointer */
      *outpScpp = tscp;
  
! 	/* insert scache in dnlc */
! 	if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
! 	    /* lock the directory entry to prevent racing callback revokes */
! 	    lock_ObtainMutex(&dscp->mx);
! 	    if ( dscp->cbServerp && dscp->cbExpires )
              cm_dnlcEnter(dscp, namep, tscp);
! 	    lock_ReleaseMutex(&dscp->mx);
! 	}
  
! 	/* and return */
      return 0;
  }
  
  long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
  {
!         long code;
!         cm_conn_t *connp;
! 	AFSFid afsFid;
!         int sflags;
!         AFSFetchStatus newDirStatus;
!         AFSVolSync volSync;
  
  #ifdef AFS_FREELANCE_CLIENT
! 	if (cm_freelanceEnabled && dscp == cm_rootSCachep) {
! 	  /* deleting a mount point from the root dir. */
! 	  code = cm_FreelanceRemoveMount(namep);
! 	  return code;
! 	}
! #endif
  
! 	/* make sure we don't screw up the dir status during the merge */
!         lock_ObtainMutex(&dscp->mx);
! 	sflags = CM_SCACHESYNC_STOREDATA;
! 	code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
!         lock_ReleaseMutex(&dscp->mx);
! 	if (code) return code;
  
! 	/* make the RPC */
! 	afsFid.Volume = dscp->fid.volume;
! 	afsFid.Vnode = dscp->fid.vnode;
! 	afsFid.Unique = dscp->fid.unique;
!         do {
! 		code = cm_Conn(&dscp->fid, userp, reqp, &connp);
!                 if (code) continue;
!                 
!                 code = RXAFS_RemoveFile(connp->callp, &afsFid, namep,
! 					&newDirStatus, &volSync);
! 		
! 	} while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
!         code = cm_MapRPCError(code, reqp);
  
!         lock_ObtainMutex(&dscp->mx);
! 	cm_dnlcRemove(dscp, namep);
! 	cm_SyncOpDone(dscp, NULL, sflags);
!         if (code == 0) cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
!         lock_ReleaseMutex(&dscp->mx);
  
!         return code;
  }
  
  /* called with a locked vnode, and fills in the link info.
--- 1002,1176 ----
              }
              tscp = NULL;   /* to force call of cm_GetSCache */
          }
!     }
  
!   haveFid:       
!     if ( !tscp )    /* we did not find it in the dnlc */
!     {
!         dnlcHit = 0;	
          code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
          if (code) 
              return code;
!     }       
      /* tscp is now held */
  
!     lock_ObtainMutex(&tscp->mx);
!     code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
                        CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
      if (code) { 
!         lock_ReleaseMutex(&tscp->mx);
!         cm_ReleaseSCache(tscp);
          return code;
!     }
      /* tscp is now locked */
  
      if (!(flags & CM_FLAG_NOMOUNTCHASE)
!          && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
!         /* mount points are funny: they have a volume name to mount
           * the root of.
           */
!         code = cm_ReadMountPoint(tscp, userp, reqp);
          if (code == 0)
!             code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
                                          &mountedScp);
!         lock_ReleaseMutex(&tscp->mx);
!         cm_ReleaseSCache(tscp);
!         if (code) {
              return code;
          }
          tscp = mountedScp;
      }
!     else {
!         lock_ReleaseMutex(&tscp->mx);
!     }
  
!     /* copy back pointer */
      *outpScpp = tscp;
  
!     /* insert scache in dnlc */
!     if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
!         /* lock the directory entry to prevent racing callback revokes */
!         lock_ObtainMutex(&dscp->mx);
!         if ( dscp->cbServerp && dscp->cbExpires )
              cm_dnlcEnter(dscp, namep, tscp);
!         lock_ReleaseMutex(&dscp->mx);
!     }
  
!     /* and return */
      return 0;
  }
  
+ int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
+ {
+     char *tp;
+     int prefixCount;
+ 
+     tp = strrchr(inp, '@');
+     if (tp == NULL) 
+         return 0;		/* no @sys */
+ 
+     if (strcmp(tp, "@sys") != 0) 
+         return 0;	/* no @sys */
+ 
+     /* caller just wants to know if this is a valid @sys type of name */
+     if (outp == NULL) 
+         return 1;
+ 
+     if (index >= MAXNUMSYSNAMES)
+         return -1;
+ 
+     /* otherwise generate the properly expanded @sys name */
+     prefixCount = tp - inp;
+ 
+     strncpy(outp, inp, prefixCount);	/* copy out "a." from "a.@sys" */
+     outp[prefixCount] = 0;		/* null terminate the "a." */
+     strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
+     return 1;
+ }   
+ 
+ long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
+                cm_req_t *reqp, cm_scache_t **outpScpp)
+ {
+     long code;
+     char tname[256];
+     int sysNameIndex = 0;
+     cm_scache_t *scp = 0;
+ 
+     for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
+         code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
+         if (code > 0) {
+             code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
+             if (code == 0) {
+                 *outpScpp = scp;
+                 return 0;
+             }
+ 			if (scp) {
+ 				cm_ReleaseSCache(scp);
+ 				scp = 0;
+ 			}
+         } else {
+             return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
+         }
+     }
+ 
+     /* None of the possible sysName expansions could be found */
+     if (flags & CM_FLAG_CHECKPATH)
+         return CM_ERROR_NOSUCHPATH;
+     else
+         return CM_ERROR_NOSUCHFILE;
+ }
+ 
  long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
  {
!     long code;
!     cm_conn_t *connp;
!     AFSFid afsFid;
!     int sflags;
!     AFSFetchStatus newDirStatus;
!     AFSVolSync volSync;
!     struct rx_connection * callp;
  
  #ifdef AFS_FREELANCE_CLIENT
!     if (cm_freelanceEnabled && dscp == cm_rootSCachep) {
!         /* deleting a mount point from the root dir. */
!         code = cm_FreelanceRemoveMount(namep);
!         return code;
!     }
! #endif  
  
!     /* make sure we don't screw up the dir status during the merge */
!     lock_ObtainMutex(&dscp->mx);
!     sflags = CM_SCACHESYNC_STOREDATA;
!     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
!     lock_ReleaseMutex(&dscp->mx);
!     if (code) 
!         return code;
  
!     /* make the RPC */
!     afsFid.Volume = dscp->fid.volume;
!     afsFid.Vnode = dscp->fid.vnode;
!     afsFid.Unique = dscp->fid.unique;
!     do {
!         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
!         if (code) 
!             continue;
  
!         callp = cm_GetRxConn(connp);
!         code = RXAFS_RemoveFile(callp, &afsFid, namep,
!                                  &newDirStatus, &volSync);
!         rx_PutConnection(callp);
! 
!     } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
!     code = cm_MapRPCError(code, reqp);
! 
!     lock_ObtainMutex(&dscp->mx);
!     cm_dnlcRemove(dscp, namep);
!     cm_SyncOpDone(dscp, NULL, sflags);
!     if (code == 0) 
!         cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
!     lock_ReleaseMutex(&dscp->mx);
  
!     return code;
  }
  
  /* called with a locked vnode, and fills in the link info.
***************
*** 1131,1425 ****
   */
  long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
  {
! 	long code;
!         cm_buf_t *bufp;
!         long temp;
!         osi_hyper_t thyper;
! 
! 	lock_AssertMutex(&linkScp->mx);
!         if (!linkScp->mountPointStringp) {
! 		/* read the link data */
! 		lock_ReleaseMutex(&linkScp->mx);
! 		thyper.LowPart = thyper.HighPart = 0;
! 		code = buf_Get(linkScp, &thyper, &bufp);
!                 lock_ObtainMutex(&linkScp->mx);
!                 if (code) return code;
! 		while (1) {
!         	        code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
! 	                	CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
! 			if (code) {
!                                 buf_Release(bufp);
! 	                        return code;
! 	                }
! 	                if (cm_HaveBuffer(linkScp, bufp, 0)) break;
!                         
!                         code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
!                         if (code) {
!                                 buf_Release(bufp);
!                                 return code;
!                         }
! 		} /* while loop to get the data */
!                 
!                 /* now if we still have no link read in,
! 		 * copy the data from the buffer */
!                 if ((temp = linkScp->length.LowPart) >= 1024) {
!                         buf_Release(bufp);
!                         return CM_ERROR_TOOBIG;
!                 }
!                 
!                 /* otherwise, it fits; make sure it is still null (could have
! 		 * lost race with someone else referencing this link above),
! 		 * and if so, copy in the data.
!                  */
!                 if (linkScp->mountPointStringp == NULL) {
! 			linkScp->mountPointStringp = malloc(temp+1);
!                         strncpy(linkScp->mountPointStringp, bufp->datap, temp);
!                         linkScp->mountPointStringp[temp] = 0;	/* null terminate */
!                 }
!                 buf_Release(bufp);
!         }	/* don't have sym link contents cached */
!         
!         return 0;
! }
  
! /* called with a held vnode and a path suffix, with the held vnode being a
!  * symbolic link.  Our goal is to generate a new path to interpret, and return
!  * this new path in newSpaceBufferp.  If the new vnode is relative to a dir
!  * other than the directory containing the symbolic link, then the new root is
!  * returned in *newRootScpp, otherwise a null is returned there.
!  */
! long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
! 	cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
! 	cm_user_t *userp, cm_req_t *reqp)
! {
! 	long code;
!         char *linkp;
! 	cm_space_t *tsp;
! 
! 	lock_ObtainMutex(&linkScp->mx);
! 	code = cm_HandleLink(linkScp, userp, reqp);
! 	if (code) goto done;
! 
! 	/* if we may overflow the buffer, bail out; buffer is signficantly
! 	 * bigger than max path length, so we don't really have to worry about
! 	 * being a little conservative here.
!          */
! 	if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
! 	     >= CM_UTILS_SPACESIZE)
! 	        return CM_ERROR_TOOBIG;
! 
!         tsp = cm_GetSpace();
!         linkp = linkScp->mountPointStringp;
!         if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
! 		if (strlen(linkp) > cm_mountRootLen)
! 			strcpy(tsp->data, linkp+cm_mountRootLen+1);
! 		else
! 			tsp->data[0] = 0;
!                 *newRootScpp = cm_rootSCachep;
!                 cm_HoldSCache(cm_rootSCachep);
!         } else if (*linkp == '\\' || *linkp == '/') {
! 	  /* formerly, this was considered to be from the AFS root,
! 	     but this seems to create problems.  instead, we will just
! 	     reject the link */
! #if 0
! 		strcpy(tsp->data, linkp+1);
!                 *newRootScpp = cm_rootSCachep;
!                 cm_HoldSCache(cm_rootSCachep);
! #else
! 		code = CM_ERROR_NOSUCHPATH;
! 		goto done;
! #endif
          }
!         else {
! 		/* a relative link */
! 		strcpy(tsp->data, linkp);
!                 *newRootScpp = NULL;
!         }
! 	if (pathSuffixp[0] != 0) {	/* if suffix string is non-null */
!         	strcat(tsp->data, "\\");
!         	strcat(tsp->data, pathSuffixp);
! 	}
! 	*newSpaceBufferp = tsp;
! 	code = 0;
  
! done:
!         lock_ReleaseMutex(&linkScp->mx);
!         return code;
  }
  
  long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
! 	cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
  {
! 	long code;
!         char *tp;			/* ptr moving through input buffer */
!         char tc;			/* temp char */
!         int haveComponent;		/* has new component started? */
! 	char component[256];		/* this is the new component */
!         char *cp;			/* component name being assembled */
!         cm_scache_t *tscp;		/* current location in the hierarchy */
!         cm_scache_t *nscp;		/* next dude down */
!         cm_scache_t *dirScp;		/* last dir we searched */
!         cm_scache_t *linkScp;		/* new root for the symlink we just
! 					 * looked up */
!         cm_space_t *psp;		/* space for current path, if we've hit
! 					 * any symlinks */
!         cm_space_t *tempsp;		/* temp vbl */
!         char *restp;			/* rest of the pathname to interpret */
!         int symlinkCount;		/* count of # of symlinks traversed */
! 	int extraFlag;			/* avoid chasing mt pts for dir cmd */
! 	int phase = 1;			/* 1 = tidPathp, 2 = pathp */
! 
! 	tp = tidPathp;
! 	if (tp == NULL) {
!         	tp = pathp;
! 		phase = 2;
! 	}
! 	if (tp == NULL) {
! 		tp = "";
! 	}
! 	haveComponent = 0;
!         psp = NULL;
!         tscp = rootSCachep;
!         cm_HoldSCache(tscp);
!         symlinkCount = 0;
!         while (1) {
!             tc = *tp++;
! 		
!             /* map Unix slashes into DOS ones so we can interpret Unix
!              * symlinks properly
!              */
!             if (tc == '/') tc = '\\';
  
!             if (!haveComponent) {
! 			if (tc == '\\') continue;
              else if (tc == 0) {
! 				if (phase == 1) {
! 					phase = 2;
! 					tp = pathp;
! 					continue;
! 				}
                  code = 0;
                  break;
              }
              else {
! 				haveComponent = 1;
                  cp = component;
                  *cp++ = tc;
              }
!             }
!             else {
!                 /* we have a component here */
!                 if (tc == 0 || tc == '\\') {
!                     /* end of the component; we're at the last
!                      * component if tc == 0.  However, if the last
!                      * is a symlink, we have more to do.
!                      */
!                     *cp++ = 0;	/* add null termination */
!                     extraFlag = 0;
!                     if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
!                         extraFlag = CM_FLAG_NOMOUNTCHASE;
!                     code = cm_Lookup(tscp, component,
!                                       flags | extraFlag,
!                                       userp, reqp, &nscp);
  
!                     if (code) {
                          cm_ReleaseSCache(tscp);
-                         if (psp) cm_FreeSpace(psp);
-                         return code;
-                     }
-                     haveComponent = 0;	/* component done */
-                     dirScp = tscp;		/* for some symlinks */
-                     tscp = nscp;	/* already held */
-                     if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
-                         code = 0;
                          cm_ReleaseSCache(dirScp);
!                         break;
                      }
! 
!                     /* now, if tscp is a symlink, we should follow
!                      * it and assemble the path again.
!                      */
!                     lock_ObtainMutex(&tscp->mx);
!                     code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
!                                       CM_SCACHESYNC_GETSTATUS
!                                       | CM_SCACHESYNC_NEEDCALLBACK);
                      if (code) {
!                         lock_ReleaseMutex(&tscp->mx);
                          cm_ReleaseSCache(tscp);
                          cm_ReleaseSCache(dirScp);
                          break;
                      }
-                     if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
-                         /* this is a symlink; assemble a new buffer */
-                         lock_ReleaseMutex(&tscp->mx);
-                         if (symlinkCount++ >= 16) {
-                             cm_ReleaseSCache(tscp);
-                             cm_ReleaseSCache(dirScp);
-                             if (psp) cm_FreeSpace(psp);
-                             return CM_ERROR_TOOBIG;
-                         }
-                         if (tc == 0) restp = "";
-                         else restp = tp;
-                         code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
-                         if (code) {
-                             /* something went wrong */
-                             cm_ReleaseSCache(tscp);
-                             cm_ReleaseSCache(dirScp);
-                             break;
-                         }
  
!                         /* otherwise, tempsp has the new path,
!                          * and linkScp is the new root from
!                          * which to interpret that path.
!                          * Continue with the namei processing,
!                          * also doing the bookkeeping for the
!                          * space allocation and tracking the
!                          * vnode reference counts.
!                          */
!                         if (psp) cm_FreeSpace(psp);
!                         psp = tempsp;
!                         tp = psp->data;
!                         cm_ReleaseSCache(tscp);
                      tscp = linkScp;	
                      /* already held
!                                          * by AssembleLink */
!                         /* now, if linkScp is null, that's
!                          * AssembleLink's way of telling us that
!                          * the sym link is relative to the dir
!                          * containing the link.  We have a ref
!                          * to it in dirScp, and we hold it now
!                          * and reuse it as the new spot in the
!                          * dir hierarchy.
!                          */
!                         if (tscp == NULL) {
!                             cm_HoldSCache(dirScp);
!                             tscp = dirScp;
!                         }
!                     }	/* if we have a sym link */
!                     else {
!                         /* not a symlink, we may be done */
!                         lock_ReleaseMutex(&tscp->mx);
!                         if (tc == 0) {
!                             if (phase == 1) {
!                                 phase = 2;
!                                 tp = pathp;
!                                 continue;
!                             }
!                             cm_ReleaseSCache(dirScp);
!                             code = 0;
!                             break;
                          }
                      }
!                     cm_ReleaseSCache(dirScp);
!                 } /* end of a component */
!                 else *cp++ = tc;
!             } /* we have a component */
!         } /* big while loop over all components */
! 
! 	/* already held */
!     if (psp) cm_FreeSpace(psp);
! 	if (code == 0) *outScpp = tscp;
!         return code;
  }
  
  /* called with a dir, and a vnode within the dir that happens to be a symlink.
--- 1178,1484 ----
   */
  long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
  {
!     long code;
!     cm_buf_t *bufp;
!     long temp;
!     osi_hyper_t thyper;
  
!     lock_AssertMutex(&linkScp->mx);
!     if (!linkScp->mountPointStringp) {
!         /* read the link data */
!         lock_ReleaseMutex(&linkScp->mx);
!         thyper.LowPart = thyper.HighPart = 0;
!         code = buf_Get(linkScp, &thyper, &bufp);
!         lock_ObtainMutex(&linkScp->mx);
!         if (code) 
!             return code;
!         while (1) {
!             code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
!                               CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
!             if (code) {
!                 buf_Release(bufp);
!                 return code;
!             }
!             if (cm_HaveBuffer(linkScp, bufp, 0)) 
!                 break;
! 
!             code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
!             if (code) {
!                 buf_Release(bufp);
!                 return code;
!             }
!         } /* while loop to get the data */
!                 
!         /* now if we still have no link read in,
!          * copy the data from the buffer */
!         if ((temp = linkScp->length.LowPart) >= 1024) {
!             buf_Release(bufp);
!             return CM_ERROR_TOOBIG;
!         }
! 
!         /* otherwise, it fits; make sure it is still null (could have
!          * lost race with someone else referencing this link above),
!          * and if so, copy in the data.
!          */
!         if (linkScp->mountPointStringp == NULL) {
!             linkScp->mountPointStringp = malloc(temp+1);
!             strncpy(linkScp->mountPointStringp, bufp->datap, temp);
!             linkScp->mountPointStringp[temp] = 0;	/* null terminate */
          }
!         buf_Release(bufp);
!     }	/* don't have sym link contents cached */
  
!     return 0;
! }       
! 
! /* called with a held vnode and a path suffix, with the held vnode being a
!  * symbolic link.  Our goal is to generate a new path to interpret, and return
!  * this new path in newSpaceBufferp.  If the new vnode is relative to a dir
!  * other than the directory containing the symbolic link, then the new root is
!  * returned in *newRootScpp, otherwise a null is returned there.
!  */
! long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
!                       cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
!                       cm_user_t *userp, cm_req_t *reqp)
! {
!     long code;
!     char *linkp;
!     cm_space_t *tsp;
! 
!     lock_ObtainMutex(&linkScp->mx);
!     code = cm_HandleLink(linkScp, userp, reqp);
!     if (code) 
!         goto done;
! 
!     /* if we may overflow the buffer, bail out; buffer is signficantly
!      * bigger than max path length, so we don't really have to worry about
!      * being a little conservative here.
!      */
!     if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
!          >= CM_UTILS_SPACESIZE)
!         return CM_ERROR_TOOBIG;
! 
!     tsp = cm_GetSpace();
!     linkp = linkScp->mountPointStringp;
!     if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
!         if (strlen(linkp) > cm_mountRootLen)
!             strcpy(tsp->data, linkp+cm_mountRootLen+1);
!         else
!             tsp->data[0] = 0;
!         *newRootScpp = cm_rootSCachep;
!         cm_HoldSCache(cm_rootSCachep);
!     } else if (*linkp == '\\' || *linkp == '/') {
!         /* formerly, this was considered to be from the AFS root,
!          * but this seems to create problems.  instead, we will just
!          * reject the link */
! #if 0   
!         strcpy(tsp->data, linkp+1);
!         *newRootScpp = cm_rootSCachep;
!         cm_HoldSCache(cm_rootSCachep);
! #else
!         code = CM_ERROR_NOSUCHPATH;
!         goto done;
! #endif  
!     }
!     else {
!         /* a relative link */
!         strcpy(tsp->data, linkp);
!         *newRootScpp = NULL;
!     }
!     if (pathSuffixp[0] != 0) {	/* if suffix string is non-null */
!         strcat(tsp->data, "\\");
!         strcat(tsp->data, pathSuffixp);
!     }
!     *newSpaceBufferp = tsp;
!     code = 0;
! 
!   done:
!     lock_ReleaseMutex(&linkScp->mx);
!     return code;
  }
  
  long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
!                cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
  {
!     long code;
!     char *tp;			/* ptr moving through input buffer */
!     char tc;			/* temp char */
!     int haveComponent;		/* has new component started? */
!     char component[256];		/* this is the new component */
!     char *cp;			/* component name being assembled */
!     cm_scache_t *tscp;		/* current location in the hierarchy */
!     cm_scache_t *nscp;		/* next dude down */
!     cm_scache_t *dirScp;		/* last dir we searched */
!     cm_scache_t *linkScp;		/* new root for the symlink we just
!     * looked up */
!     cm_space_t *psp;		/* space for current path, if we've hit
!     * any symlinks */
!     cm_space_t *tempsp;		/* temp vbl */
!     char *restp;			/* rest of the pathname to interpret */
!     int symlinkCount;		/* count of # of symlinks traversed */
!     int extraFlag;			/* avoid chasing mt pts for dir cmd */
!     int phase = 1;			/* 1 = tidPathp, 2 = pathp */
! 
!     tp = tidPathp;
!     if (tp == NULL) {
!         tp = pathp;
!         phase = 2;
!     }
!     if (tp == NULL) {
!         tp = "";
!     }
!     haveComponent = 0;
!     psp = NULL;
!     tscp = rootSCachep;
!     cm_HoldSCache(tscp);
!     symlinkCount = 0;
!     while (1) {
!         tc = *tp++;
! 
!         /* map Unix slashes into DOS ones so we can interpret Unix
!          * symlinks properly
!          */
!         if (tc == '/') 
!             tc = '\\';
  
!         if (!haveComponent) {
!             if (tc == '\\') 
!                 continue;
              else if (tc == 0) {
!                 if (phase == 1) {
!                     phase = 2;
!                     tp = pathp;
!                     continue;
!                 }
                  code = 0;
                  break;
              }
              else {
!                 haveComponent = 1;
                  cp = component;
                  *cp++ = tc;
              }
!         }
!         else {
!             /* we have a component here */
!             if (tc == 0 || tc == '\\') {
!                 /* end of the component; we're at the last
!                  * component if tc == 0.  However, if the last
!                  * is a symlink, we have more to do.
!                  */
!                 *cp++ = 0;	/* add null termination */
!                 extraFlag = 0;
!                 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
!                     extraFlag = CM_FLAG_NOMOUNTCHASE;
!                 code = cm_Lookup(tscp, component,
!                                   flags | extraFlag,
!                                   userp, reqp, &nscp);
  
!                 if (code) {
!                     cm_ReleaseSCache(tscp);
!                     if (psp) 
!                         cm_FreeSpace(psp);
!                     return code;
!                 }
!                 haveComponent = 0;	/* component done */
!                 dirScp = tscp;		/* for some symlinks */
!                 tscp = nscp;	/* already held */
!                 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
!                     code = 0;
!                     cm_ReleaseSCache(dirScp);
!                     break;
!                 }
! 
!                 /* now, if tscp is a symlink, we should follow
!                  * it and assemble the path again.
!                  */
!                 lock_ObtainMutex(&tscp->mx);
!                 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
!                                   CM_SCACHESYNC_GETSTATUS
!                                   | CM_SCACHESYNC_NEEDCALLBACK);
!                 if (code) {
!                     lock_ReleaseMutex(&tscp->mx);
!                     cm_ReleaseSCache(tscp);
!                     cm_ReleaseSCache(dirScp);
!                     break;
!                 }
!                 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
!                     /* this is a symlink; assemble a new buffer */
!                     lock_ReleaseMutex(&tscp->mx);
!                     if (symlinkCount++ >= 16) {
                          cm_ReleaseSCache(tscp);
                          cm_ReleaseSCache(dirScp);
!                         if (psp) 
!                             cm_FreeSpace(psp);
!                         return CM_ERROR_TOOBIG;
                      }
!                     if (tc == 0) 
!                         restp = "";
!                     else 
!                         restp = tp;
!                     code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
                      if (code) {
!                         /* something went wrong */
                          cm_ReleaseSCache(tscp);
                          cm_ReleaseSCache(dirScp);
                          break;
                      }
  
!                     /* otherwise, tempsp has the new path,
!                      * and linkScp is the new root from
!                      * which to interpret that path.
!                      * Continue with the namei processing,
!                      * also doing the bookkeeping for the
!                      * space allocation and tracking the
!                      * vnode reference counts.
!                      */
!                     if (psp) 
!                         cm_FreeSpace(psp);
!                     psp = tempsp;
!                     tp = psp->data;
!                     cm_ReleaseSCache(tscp);
                      tscp = linkScp;	
                      /* already held
!                      * by AssembleLink
!                      * now, if linkScp is null, that's
!                      * AssembleLink's way of telling us that
!                      * the sym link is relative to the dir
!                      * containing the link.  We have a ref
!                      * to it in dirScp, and we hold it now
!                      * and reuse it as the new spot in the
!                      * dir hierarchy.
!                      */
!                     if (tscp == NULL) {
!                         cm_HoldSCache(dirScp);
!                         tscp = dirScp;
!                     }
!                 }	/* if we have a sym link */
!                 else {
!                     /* not a symlink, we may be done */
!                     lock_ReleaseMutex(&tscp->mx);
!                     if (tc == 0) {
!                         if (phase == 1) {
!                             phase = 2;
!                             tp = pathp;
!                             continue;
                          }
+                         cm_ReleaseSCache(dirScp);
+                         code = 0;
+                         break;
                      }
!                 }
!                 cm_ReleaseSCache(dirScp);
!             } /* end of a component */
!             else *cp++ = tc;
!         } /* we have a component */
!     } /* big while loop over all components */
! 
!     /* already held */
!     if (psp) 
!         cm_FreeSpace(psp);
!     if (code == 0) 
!         *outScpp = tscp;
!     return code;
  }
  
  /* called with a dir, and a vnode within the dir that happens to be a symlink.
***************
*** 1438,1472 ****
   * The input vnode should not be locked when this function is called.
   */
  long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
! 	cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
  {
! 	long code;
!         cm_space_t *spacep;
!         cm_scache_t *newRootScp;
  
! 	osi_Log1(afsd_logp, "Evaluating symlink vp %x", linkScp);
! 
!         code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
!         if (code) return code;
!         
!         /* now, if newRootScp is NULL, we're really being told that the symlink
! 	 * is relative to the current directory (dscp).
!          */
! 	if (newRootScp == NULL) {
! 		newRootScp = dscp;
! 		cm_HoldSCache(dscp);
!         }
!         
!         code = cm_NameI(newRootScp, spacep->data,
! 		CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
! 		userp, NULL, reqp, outScpp);
! 
! 	/* this stuff is allocated no matter what happened on the namei call,
! 	 * so free it */
! 	cm_FreeSpace(spacep);
!         cm_ReleaseSCache(newRootScp);
  
          return code;
  }
  
  /* make this big enough so that one buffer of dir pages won't overflow.  We'll
--- 1497,1532 ----
   * The input vnode should not be locked when this function is called.
   */
  long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
!                          cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
  {
!     long code;
!     cm_space_t *spacep;
!     cm_scache_t *newRootScp;
  
!     osi_Log1(afsd_logp, "Evaluating symlink vp %x", linkScp);
  
+     code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
+     if (code) 
          return code;
+ 
+     /* now, if newRootScp is NULL, we're really being told that the symlink
+      * is relative to the current directory (dscp).
+      */
+     if (newRootScp == NULL) {
+         newRootScp = dscp;
+         cm_HoldSCache(dscp);
+     }
+ 
+     code = cm_NameI(newRootScp, spacep->data,
+                      CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
+                      userp, NULL, reqp, outScpp);
+ 
+     /* this stuff is allocated no matter what happened on the namei call,
+      * so free it */
+     cm_FreeSpace(spacep);
+     cm_ReleaseSCache(newRootScp);
+ 
+     return code;
  }
  
  /* make this big enough so that one buffer of dir pages won't overflow.  We'll
***************
*** 1477,1489 ****
  
  /* rock for bulk stat calls */
  typedef struct cm_bulkStat {
! 	osi_hyper_t bufOffset;	/* only do it for things in this buffer page */
  
! 	/* info for the actual call */
!         int counter;			/* next free slot */
!         AFSFid fids[CM_BULKMAX];
!         AFSFetchStatus stats[CM_BULKMAX];
! 	AFSCallBack callbacks[CM_BULKMAX];
  } cm_bulkStat_t;
  
  /* for a given entry, make sure that it isn't in the stat cache, and then
--- 1537,1549 ----
  
  /* rock for bulk stat calls */
  typedef struct cm_bulkStat {
!     osi_hyper_t bufOffset;	/* only do it for things in this buffer page */
  
!     /* info for the actual call */
!     int counter;			/* next free slot */
!     AFSFid fids[CM_BULKMAX];
!     AFSFetchStatus stats[CM_BULKMAX];
!     AFSCallBack callbacks[CM_BULKMAX];
  } cm_bulkStat_t;
  
  /* for a given entry, make sure that it isn't in the stat cache, and then
***************
*** 1494,2626 ****
   * processing, to avoid deadlocks.
   */
  long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
! 	osi_hyper_t *offp)
  {
! 	osi_hyper_t thyper;
!         cm_bulkStat_t *bsp;
!         int i;
!         cm_scache_t *tscp;
!         cm_fid_t tfid;
  
! 	bsp = rockp;
  
! 	/* Don't overflow bsp. */
! 	if (bsp->counter >= CM_BULKMAX)
! 		return CM_ERROR_STOPNOW;
  
!         thyper.LowPart = buf_bufferSize;
!         thyper.HighPart = 0;
!         thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
!         
!         /* thyper is now the first byte past the end of the record we're
! 	 * interested in, and bsp->bufOffset is the first byte of the record
! 	 * we're interested in.
!          * Skip data in the others.
!          * Skip '.' and '..'
!          */
!         if (LargeIntegerLessThan(*offp, bsp->bufOffset))
! 		return 0;
!         if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
! 		return CM_ERROR_STOPNOW;
!         if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
! 		return 0;
!         
! 	tfid.cell = scp->fid.cell;
! 	tfid.volume = scp->fid.volume;
!         tfid.vnode = ntohl(dep->fid.vnode);
!         tfid.unique = ntohl(dep->fid.unique);
!         tscp = cm_FindSCache(&tfid);
!         if (tscp) {
!         	if (lock_TryMutex(&tscp->mx)) {
! 			/* we have an entry that we can look at */
! 			if (cm_HaveCallback(tscp)) {
! 				/* we have a callback on it.  Don't bother
! 				 * fetching this stat entry, since we're happy
! 				 * with the info we have.
!                                  */
! 				lock_ReleaseMutex(&tscp->mx);
!                                 cm_ReleaseSCache(tscp);
!                                 return 0;
!                         }
!                         lock_ReleaseMutex(&tscp->mx);
! 		}	/* got lock */
                  cm_ReleaseSCache(tscp);
!         }	/* found entry */
  
  #ifdef AFS_FREELANCE_CLIENT
! 	// yj: if this is a mountpoint under root.afs then we don't want it
! 	// to be bulkstat-ed, instead, we call getSCache directly and under
! 	// getSCache, it is handled specially.
! 	if 	( cm_freelanceEnabled &&
            tfid.cell==AFS_FAKE_ROOT_CELL_ID && 
            tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
            !(tfid.vnode==0x1 && tfid.unique==0x1) )
! 	{
          osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
! 		return cm_GetSCache(&tfid, &tscp, NULL, NULL);
! 	}
  #endif /* AFS_FREELANCE_CLIENT */
  
! 	i = bsp->counter++;
!         bsp->fids[i].Volume = scp->fid.volume;
!         bsp->fids[i].Vnode = tfid.vnode;
!         bsp->fids[i].Unique = tfid.unique;
!         return 0;
! }
  
  /* called with a locked scp and a pointer to a buffer.  Make bulk stat
   * calls on all undeleted files in the page of the directory specified.
   */
  void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
! 	cm_req_t *reqp)
  {
! 	long code;
!         cm_bulkStat_t bb;	/* this is *BIG*, probably 12K or so;
! 				 * watch for stack problems */
!         AFSCBFids fidStruct;
!         AFSBulkStats statStruct;
!         cm_conn_t *connp;
!         AFSCBs callbackStruct;
!         long filex;
! 	AFSVolSync volSync;
!         cm_callbackRequest_t cbReq;
!         long filesThisCall;
! 	long i;
!         long j;
!         cm_scache_t *scp;
!         cm_fid_t tfid;
! 
! 	osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
! 
! 	/* should be on a buffer boundary */
! 	osi_assert((offsetp->LowPart & (buf_bufferSize - 1)) == 0);
! 
! 	bb.counter = 0;
!         bb.bufOffset = *offsetp;
! 
! 	/* first, assemble the file IDs we need to stat */
!         code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp,
! 			   reqp, NULL);
  
! 	/* if we failed, bail out early */
!         if (code && code != CM_ERROR_STOPNOW) return;
!         
!         /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
! 	 * make the calls to create the entries.  Handle AFSCBMAX files at a
! 	 * time.
           */
! 	filex = 0;
! 	while(filex < bb.counter) {
! 		filesThisCall = bb.counter - filex;
!                 if (filesThisCall > AFSCBMAX) filesThisCall = AFSCBMAX;
! 
! 		fidStruct.AFSCBFids_len = filesThisCall;
!                 fidStruct.AFSCBFids_val = &bb.fids[filex];
!                 statStruct.AFSBulkStats_len = filesThisCall;
!                 statStruct.AFSBulkStats_val = &bb.stats[filex];
!                 callbackStruct.AFSCBs_len = filesThisCall;
!                 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
!                 cm_StartCallbackGrantingCall(NULL, &cbReq);
! 		osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
! 		do {
! 			code = cm_Conn(&dscp->fid, userp, reqp, &connp);
! 	                if (code) continue;
! 		
! 	                code = RXAFS_BulkStatus(connp->callp, &fidStruct,
!                         	&statStruct, &callbackStruct, &volSync);
  
! 		} while (cm_Analyze(connp, userp, reqp, &dscp->fid,
! 				    &volSync, NULL, &cbReq, code));
!                 code = cm_MapRPCError(code, reqp);
  
!                 osi_Log0(afsd_logp, "CALL BulkStatus DONE");
!                 
! 		/* may as well quit on an error, since we're not going to do
!                  * much better on the next immediate call, either.
!                  */
!                 if (code) break;
!                 
!                 /* otherwise, we should do the merges */
!                 for(i = 0; i<filesThisCall; i++) {
! 			j = filex + i;
! 			tfid.cell = dscp->fid.cell;
!                         tfid.volume = bb.fids[j].Volume;
!                         tfid.vnode = bb.fids[j].Vnode;
!                         tfid.unique = bb.fids[j].Unique;
! 			code = cm_GetSCache(&tfid, &scp, userp, reqp);
!                         if (code != 0) continue;
!                         
!                         /* otherwise, if this entry has no callback info, 
!                          * merge in this.
!                          */
!                         lock_ObtainMutex(&scp->mx);
! 			/* now, we have to be extra paranoid on merging in this
! 			 * information, since we didn't use cm_SyncOp before
! 			 * starting the fetch to make sure that no bad races
! 			 * were occurring.  Specifically, we need to make sure
! 			 * we don't obliterate any newer information in the
! 			 * vnode than have here.
!                          *
!                          * Right now, be pretty conservative: if there's a
! 			 * callback or a pending call, skip it.
!                          */
! 			if (scp->cbServerp == NULL
!                         	&& !(scp->flags &
!                                 	(CM_SCACHEFLAG_FETCHING
! 					 | CM_SCACHEFLAG_STORING
!                                          | CM_SCACHEFLAG_SIZESTORING))) {
! 				cm_EndCallbackGrantingCall(scp, &cbReq,
! 					&bb.callbacks[j],
!                                 	CM_CALLBACK_MAINTAINCOUNT);
! 	                        cm_MergeStatus(scp, &bb.stats[j], &volSync,
! 					userp, 0);
! 			}
!                         lock_ReleaseMutex(&scp->mx);
!                         cm_ReleaseSCache(scp);
!                 } /* all files in the response */
! 		/* now tell it to drop the count,
! 		 * after doing the vnode processing above */
          cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
!                 
!                 filex += filesThisCall;
!         }	/* while there are still more files to process */
!         osi_Log0(afsd_logp, "END cm_TryBulkStat");
! }
  
  void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
  {
! 	long mask;
  
! 	/* initialize store back mask as inexpensive local variable */
!         mask = 0;
! 	memset(statusp, 0, sizeof(AFSStoreStatus));
! 
! 	/* copy out queued info from scache first, if scp passed in */
!         if (scp) {
!         	if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
! 			statusp->ClientModTime = scp->clientModTime;
! 	                mask |= AFS_SETMODTIME;
! 	                scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
! 	        }
! 	}
! 
! 	if (attrp) {
! 		/* now add in our locally generated request */
! 	        if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
! 	        	statusp->ClientModTime = attrp->clientModTime;
! 	                mask |= AFS_SETMODTIME;
! 		}
! 	        if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
! 	        	statusp->UnixModeBits = attrp->unixModeBits;
! 	                mask |= AFS_SETMODE;
! 		}
! 	        if (attrp->mask & CM_ATTRMASK_OWNER) {
! 	        	statusp->Owner = attrp->owner;
! 	                mask |= AFS_SETOWNER;
! 		}
! 	        if (attrp->mask & CM_ATTRMASK_GROUP) {
! 	        	statusp->Group = attrp->group;
! 	                mask |= AFS_SETGROUP;
! 		}
! 	}
! 	statusp->Mask = mask;
! }
  
  /* set the file size, and make sure that all relevant buffers have been
   * truncated.  Ensure that any partially truncated buffers have been zeroed
   * to the end of the buffer.
   */
  long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
! 	cm_req_t *reqp)
  {
! 	long code;
! 	int shrinking;
  
! 	/* start by locking out buffer creation */
! 	lock_ObtainWrite(&scp->bufCreateLock);
  
! 	/* verify that this is a file, not a dir or a symlink */
! 	lock_ObtainMutex(&scp->mx);
! 	code = cm_SyncOp(scp, NULL, userp, reqp, 0,
!         	CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
!         if (code) goto done;
!         
!         if (scp->fileType != CM_SCACHETYPE_FILE) {
! 		code = CM_ERROR_ISDIR;
!                 goto done;
!         }
  
! startover:
!         if (LargeIntegerLessThan(*sizep, scp->length))
! 		shrinking = 1;
! 	else
! 		shrinking = 0;
  
!         lock_ReleaseMutex(&scp->mx);
  
! 	/* can't hold scp->mx lock here, since we may wait for a storeback to
! 	 * finish if the buffer package is cleaning a buffer by storing it to
! 	 * the server.
!          */
! 	if (shrinking)
! 		buf_Truncate(scp, userp, reqp, sizep);
!         
!         /* now ensure that file length is short enough, and update truncPos */
!         lock_ObtainMutex(&scp->mx);
! 	
! 	/* make sure we have a callback (so we have the right value for the
! 	 * length), and wait for it to be safe to do a truncate.
!          */
! 	code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
! 		CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
! 		| CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
! 	if (code) goto done;
! 
! 	if (LargeIntegerLessThan(*sizep, scp->length)) {
! 		/* a real truncation.  If truncPos is not set yet, or is bigger
! 		 * than where we're truncating the file, set truncPos to this
! 		 * new value.
! 		 */
! 		if (!shrinking)
! 			goto startover;
! 		if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
!                 	|| LargeIntegerLessThan(*sizep, scp->length)) {
! 			/* set trunc pos */
!                         scp->truncPos = *sizep;
!                         scp->mask |= CM_SCACHEMASK_TRUNCPOS;
! 		}
!                 /* in either case, the new file size has been changed */
!                 scp->length = *sizep;
!                 scp->mask |= CM_SCACHEMASK_LENGTH;
!         }
!         else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
! 		/* really extending the file */
!                 scp->length = *sizep;
!                 scp->mask |= CM_SCACHEMASK_LENGTH;
! 	}
  
! 	/* done successfully */
!         code = 0;
  
! done:
!         lock_ReleaseMutex(&scp->mx);
! 	lock_ReleaseWrite(&scp->bufCreateLock);
  
!         return code;
  }
  
  /* set the file size or other attributes (but not both at once) */
  long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
! 	cm_req_t *reqp)
  {
! 	long code;
! 	int flags;
!         AFSFetchStatus afsOutStatus;
!         AFSVolSync volSync;
!         cm_conn_t *connp;
!         AFSFid tfid;
!         AFSStoreStatus afsInStatus;
! 
! 	/* handle file length setting */
! 	if (attrp->mask & CM_ATTRMASK_LENGTH)
!         	return cm_SetLength(scp, &attrp->length, userp, reqp);
  
! 	flags = CM_SCACHESYNC_STORESTATUS;
  
!         lock_ObtainMutex(&scp->mx);
! 	/* otherwise, we have to make an RPC to get the status */
! 	code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
  
! 	/* make the attr structure */
!         cm_StatusFromAttr(&afsInStatus, scp, attrp);
  
! 	lock_ReleaseMutex(&scp->mx);
!         if (code) return code;
! 		
! 	/* now make the RPC */
! 	osi_Log1(afsd_logp, "CALL StoreStatus vp %x", (long) scp);
! 	tfid.Volume = scp->fid.volume;
!         tfid.Vnode = scp->fid.vnode;
!         tfid.Unique = scp->fid.unique;
!         do {
! 		code = cm_Conn(&scp->fid, userp, reqp, &connp);
!                 if (code) continue;
! 		
!                 code = RXAFS_StoreStatus(connp->callp, &tfid,
! 			&afsInStatus, &afsOutStatus, &volSync);
  
! 	} while (cm_Analyze(connp, userp, reqp,
! 			    &scp->fid, &volSync, NULL, NULL, code));
!         code = cm_MapRPCError(code, reqp);
  
! 	osi_Log1(afsd_logp, "CALL StoreStatus DONE, code %d", code);
  
! 	lock_ObtainMutex(&scp->mx);
! 	cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
! 	if (code == 0)
! 		cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
! 				CM_MERGEFLAG_FORCE);
! 	
!         /* if we're changing the mode bits, discard the ACL cache, 
!          * since we changed the mode bits.
!          */
!         if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
! 	lock_ReleaseMutex(&scp->mx);
          return code;
! }
  
! long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
! 	cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
! {
! 	cm_conn_t *connp;
!         long code;
!         AFSFid dirAFSFid;
!         cm_callbackRequest_t cbReq;
!         AFSFid newAFSFid;
!         cm_fid_t newFid;
!         cm_scache_t *scp;
!         int didEnd;
! 	AFSStoreStatus inStatus;
!         AFSFetchStatus updatedDirStatus;
!         AFSFetchStatus newFileStatus;
!         AFSCallBack newFileCallback;
!         AFSVolSync volSync;
  
! 	/* can't create names with @sys in them; must expand it manually first.
!          * return "invalid request" if they try.
!          */
! 	if (cm_ExpandSysName(namep, NULL, 0)) {
! 		return CM_ERROR_ATSYS;
!         }
  
! 	/* before starting the RPC, mark that we're changing the file data, so
! 	 * that someone who does a chmod will know to wait until our call
! 	 * completes.
!          */
! 	lock_ObtainMutex(&dscp->mx);
! 	code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
          if (code == 0) {
! 		cm_StartCallbackGrantingCall(NULL, &cbReq);
          }
! 	lock_ReleaseMutex(&dscp->mx);
!         if (code) {
!         	return code;
! 	}
!         didEnd = 0;
! 
! 	cm_StatusFromAttr(&inStatus, NULL, attrp);
! 
! 	/* try the RPC now */
!         do {
! 		code = cm_Conn(&dscp->fid, userp, reqp, &connp);
!                 if (code) continue;
!                 
! 		dirAFSFid.Volume = dscp->fid.volume;
!                 dirAFSFid.Vnode = dscp->fid.vnode;
!                 dirAFSFid.Unique = dscp->fid.unique;
!                 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
! 					&inStatus, &newAFSFid, &newFileStatus,
! 					&updatedDirStatus, &newFileCallback,
! 					&volSync);
! 	} while (cm_Analyze(connp, userp, reqp,
! 			    &dscp->fid, &volSync, NULL, &cbReq, code));
!         code = cm_MapRPCError(code, reqp);
!         
!         lock_ObtainMutex(&dscp->mx);
!         cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
! 	if (code == 0) {
! 	        cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
! 	}
!         lock_ReleaseMutex(&dscp->mx);
  
! 	/* now try to create the file's entry, too, but be careful to 
! 	 * make sure that we don't merge in old info.  Since we weren't locking
! 	 * out any requests during the file's creation, we may have pretty old
! 	 * info.
! 	 */
! 	if (code == 0) {
! 		newFid.cell = dscp->fid.cell;
!                 newFid.volume = dscp->fid.volume;
!                 newFid.vnode = newAFSFid.Vnode;
!                 newFid.unique = newAFSFid.Unique;
! 		code = cm_GetSCache(&newFid, &scp, userp, reqp);
!                 if (code == 0) {
! 			lock_ObtainMutex(&scp->mx);
! 			if (!cm_HaveCallback(scp)) {
! 				cm_MergeStatus(scp, &newFileStatus, &volSync,
! 						userp, 0);
! 				cm_EndCallbackGrantingCall(scp, &cbReq,
! 							&newFileCallback, 0);
!                                 didEnd = 1;
!                         }
! 			lock_ReleaseMutex(&scp->mx);
! 			*scpp = scp;
!                 }
!         }
! 	
!         /* make sure we end things properly */
!         if (!didEnd)
!         	cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
  
!         return code;
! }
  
  long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
  {
! 	long code;
  
! 	lock_ObtainWrite(&scp->bufCreateLock);
! 	code = buf_CleanVnode(scp, userp, reqp);
! 	lock_ReleaseWrite(&scp->bufCreateLock);
! 	if (code == 0) {
! 		lock_ObtainMutex(&scp->mx);
! 		scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
! 				 | CM_SCACHEFLAG_OUTOFSPACE);
! 		if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
! 				   | CM_SCACHEMASK_CLIENTMODTIME
! 				   | CM_SCACHEMASK_LENGTH))
! 			code = cm_StoreMini(scp, userp, reqp);
! 		lock_ReleaseMutex(&scp->mx);
! 	}
!         return code;
  }
  
  long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
! 	cm_user_t *userp, cm_req_t *reqp)
  {
! 	cm_conn_t *connp;
!         long code;
!         AFSFid dirAFSFid;
!         cm_callbackRequest_t cbReq;
!         AFSFid newAFSFid;
!         cm_fid_t newFid;
!         cm_scache_t *scp;
!         int didEnd;
! 	AFSStoreStatus inStatus;
!         AFSFetchStatus updatedDirStatus;
!         AFSFetchStatus newDirStatus;
!         AFSCallBack newDirCallback;
!         AFSVolSync volSync;
  
! 	/* can't create names with @sys in them; must expand it manually first.
!          * return "invalid request" if they try.
!          */
! 	if (cm_ExpandSysName(namep, NULL, 0)) {
! 		return CM_ERROR_ATSYS;
!         }
  
! 	/* before starting the RPC, mark that we're changing the directory
! 	 * data, so that someone who does a chmod on the dir will wait until
! 	 * our call completes.
!          */
! 	lock_ObtainMutex(&dscp->mx);
! 	code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
          if (code == 0) {
! 		cm_StartCallbackGrantingCall(NULL, &cbReq);
          }
! 	lock_ReleaseMutex(&dscp->mx);
!         if (code) {
!         	return code;
! 	}
!         didEnd = 0;
  
! 	cm_StatusFromAttr(&inStatus, NULL, attrp);
  
! 	/* try the RPC now */
!         do {
! 		code = cm_Conn(&dscp->fid, userp, reqp, &connp);
!                 if (code) continue;
!                 
! 		dirAFSFid.Volume = dscp->fid.volume;
!                 dirAFSFid.Vnode = dscp->fid.vnode;
!                 dirAFSFid.Unique = dscp->fid.unique;
!                 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
! 				     &inStatus, &newAFSFid, &newDirStatus,
! 				     &updatedDirStatus, &newDirCallback,
! 				     &volSync);
! 	} while (cm_Analyze(connp, userp, reqp,
! 			    &dscp->fid, &volSync, NULL, &cbReq, code));
!         code = cm_MapRPCError(code, reqp);
!         
!         lock_ObtainMutex(&dscp->mx);
!         cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
! 	if (code == 0) {
! 	        cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
! 	}
!         lock_ReleaseMutex(&dscp->mx);
  
! 	/* now try to create the new dir's entry, too, but be careful to 
! 	 * make sure that we don't merge in old info.  Since we weren't locking
! 	 * out any requests during the file's creation, we may have pretty old
! 	 * info.
! 	 */
! 	if (code == 0) {
! 		newFid.cell = dscp->fid.cell;
!                 newFid.volume = dscp->fid.volume;
!                 newFid.vnode = newAFSFid.Vnode;
!                 newFid.unique = newAFSFid.Unique;
! 		code = cm_GetSCache(&newFid, &scp, userp, reqp);
!                 if (code == 0) {
! 			lock_ObtainMutex(&scp->mx);
! 			if (!cm_HaveCallback(scp)) {
! 				cm_MergeStatus(scp, &newDirStatus, &volSync,
! 						userp, 0);
! 				cm_EndCallbackGrantingCall(scp, &cbReq,
! 							&newDirCallback, 0);
!                                 didEnd = 1;
!                         }
! 			lock_ReleaseMutex(&scp->mx);
! 			cm_ReleaseSCache(scp);
!                 }
!         }
! 	
!         /* make sure we end things properly */
!         if (!didEnd)
!         	cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
! 	
!         /* and return error code */
          return code;
  }
  
  long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
! 	cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
  {
! 	cm_conn_t *connp;
!         long code;
!         AFSFid dirAFSFid;
!         AFSFid newAFSFid;
!         cm_fid_t newFid;
!         cm_scache_t *scp;
! 	AFSStoreStatus inStatus;
!         AFSFetchStatus updatedDirStatus;
!         AFSFetchStatus newLinkStatus;
!         AFSVolSync volSync;
! 
! 	/* before starting the RPC, mark that we're changing the directory data,
! 	 * so that someone who does a chmod on the dir will wait until our
! 	 * call completes.
!          */
! 	lock_ObtainMutex(&dscp->mx);
! 	code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
! 	lock_ReleaseMutex(&dscp->mx);
!         if (code) {
!         	return code;
! 	}
  
! 	cm_StatusFromAttr(&inStatus, NULL, attrp);
  
! 	/* try the RPC now */
!         do {
! 		code = cm_Conn(&dscp->fid, userp, reqp, &connp);
!                 if (code) continue;
!                 
! 		dirAFSFid.Volume = dscp->fid.volume;
!                 dirAFSFid.Vnode = dscp->fid.vnode;
!                 dirAFSFid.Unique = dscp->fid.unique;
!                 code = RXAFS_Symlink(connp->callp, &dirAFSFid, namep, contentsp,
! 				     &inStatus, &newAFSFid, &newLinkStatus,
! 				     &updatedDirStatus, &volSync);
! 	} while (cm_Analyze(connp, userp, reqp,
! 			    &dscp->fid, &volSync, NULL, NULL, code));
!         code = cm_MapRPCError(code, reqp);
!         
!         lock_ObtainMutex(&dscp->mx);
!         cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
! 	if (code == 0) {
! 	        cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
! 	}
!         lock_ReleaseMutex(&dscp->mx);
  
! 	/* now try to create the new dir's entry, too, but be careful to 
! 	 * make sure that we don't merge in old info.  Since we weren't locking
! 	 * out any requests during the file's creation, we may have pretty old
! 	 * info.
! 	 */
! 	if (code == 0) {
! 		newFid.cell = dscp->fid.cell;
!                 newFid.volume = dscp->fid.volume;
!                 newFid.vnode = newAFSFid.Vnode;
!                 newFid.unique = newAFSFid.Unique;
! 		code = cm_GetSCache(&newFid, &scp, userp, reqp);
!                 if (code == 0) {
! 			lock_ObtainMutex(&scp->mx);
! 			if (!cm_HaveCallback(scp)) {
! 				cm_MergeStatus(scp, &newLinkStatus, &volSync,
! 						userp, 0);
!                         }
! 			lock_ReleaseMutex(&scp->mx);
! 			cm_ReleaseSCache(scp);
!                 }
          }
  	
!         /* and return error code */
!         return code;
  }
  
  long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
! 	cm_req_t *reqp)
  {
! 	cm_conn_t *connp;
!         long code;
!         AFSFid dirAFSFid;
!         int didEnd;
!         AFSFetchStatus updatedDirStatus;
!         AFSVolSync volSync;
! 
! 	/* before starting the RPC, mark that we're changing the directory data,
! 	 * so that someone who does a chmod on the dir will wait until our
! 	 * call completes.
!          */
! 	lock_ObtainMutex(&dscp->mx);
! 	code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
! 	lock_ReleaseMutex(&dscp->mx);
!         if (code) {
!         	return code;
! 	}
!         didEnd = 0;
  
! 	/* try the RPC now */
!         do {
! 		code = cm_Conn(&dscp->fid, userp, reqp, &connp);
!                 if (code) continue;
!                 
! 		dirAFSFid.Volume = dscp->fid.volume;
!                 dirAFSFid.Vnode = dscp->fid.vnode;
!                 dirAFSFid.Unique = dscp->fid.unique;
!                 code = RXAFS_RemoveDir(connp->callp, &dirAFSFid, namep,
! 					&updatedDirStatus, &volSync);
! 	} while (cm_Analyze(connp, userp, reqp,
! 			    &dscp->fid, &volSync, NULL, NULL, code));
!         code = cm_MapRPCErrorRmdir(code, reqp);
!         
!         lock_ObtainMutex(&dscp->mx);
! 	cm_dnlcRemove(dscp, namep); 
!         cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
! 	if (code == 0) {
! 	        cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
! 	}
!         lock_ReleaseMutex(&dscp->mx);
  
!         /* and return error code */
!         return code;
  }
  
  long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
  {
! 	/* grab mutex on contents */
! 	lock_ObtainMutex(&scp->mx);
  
! 	/* reset the prefetch info */
! 	scp->prefetch.base.LowPart = 0;		/* base */
! 	scp->prefetch.base.HighPart = 0;
! 	scp->prefetch.end.LowPart = 0;		/* and end */
! 	scp->prefetch.end.HighPart = 0;
!         
!         /* release mutex on contents */
! 	lock_ReleaseMutex(&scp->mx);
! 	
!         /* we're done */
!         return 0;
! }
  
  long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
! 	char *newNamep, cm_user_t *userp, cm_req_t *reqp)
  {
! 	cm_conn_t *connp;
!         long code;
!         AFSFid oldDirAFSFid;
!         AFSFid newDirAFSFid;
!         int didEnd;
!         AFSFetchStatus updatedOldDirStatus;
!         AFSFetchStatus updatedNewDirStatus;
!         AFSVolSync volSync;
!         int oneDir;
! 
! 	/* before starting the RPC, mark that we're changing the directory data,
! 	 * so that someone who does a chmod on the dir will wait until our call
! 	 * completes.  We do this in vnode order so that we don't deadlock,
! 	 * which makes the code a little verbose.
!          */
! 	if (oldDscp == newDscp) {
!                 /* check for identical names */
!                 if (strcmp(oldNamep, newNamep) == 0)
!                         return CM_ERROR_RENAME_IDENTICAL;
! 
! 		oneDir = 1;
! 		lock_ObtainMutex(&oldDscp->mx);
! 		cm_dnlcRemove(oldDscp, oldNamep);
! 		cm_dnlcRemove(oldDscp, newNamep);
! 		code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
! 				 CM_SCACHESYNC_STOREDATA);
! 		lock_ReleaseMutex(&oldDscp->mx);
          }
          else {
!         	/* two distinct dir vnodes */
!                 oneDir = 0;
! 		if (oldDscp->fid.cell != newDscp->fid.cell ||
!                 	oldDscp->fid.volume != newDscp->fid.volume)
!                         	return CM_ERROR_CROSSDEVLINK;
! 
! 		/* shouldn't happen that we have distinct vnodes for two
! 		 * different files, but could due to deliberate attack, or
! 		 * stale info.  Avoid deadlocks and quit now.
!                  */
! 		if (oldDscp->fid.vnode == newDscp->fid.vnode)
!                 	return CM_ERROR_CROSSDEVLINK;
!                         
! 		if (oldDscp->fid.vnode < newDscp->fid.vnode) {
! 			lock_ObtainMutex(&oldDscp->mx);
! 			cm_dnlcRemove(oldDscp, oldNamep);
! 			code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
! 					 CM_SCACHESYNC_STOREDATA);
! 			lock_ReleaseMutex(&oldDscp->mx);
!                         if (code == 0) {
! 				lock_ObtainMutex(&newDscp->mx);
! 				cm_dnlcRemove(newDscp, newNamep);
! 				code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
! 						 CM_SCACHESYNC_STOREDATA);
! 				lock_ReleaseMutex(&newDscp->mx);
!                                 if (code) {
! 					/* cleanup first one */
!                                         cm_SyncOpDone(oldDscp, NULL,
! 						      CM_SCACHESYNC_STOREDATA);
!                                 }
!                         }
! 		}
!                 else {
! 			/* lock the new vnode entry first */
! 			lock_ObtainMutex(&newDscp->mx);
! 			cm_dnlcRemove(newDscp, newNamep);
! 			code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
! 					 CM_SCACHESYNC_STOREDATA);
! 			lock_ReleaseMutex(&newDscp->mx);
!                         if (code == 0) {
! 				lock_ObtainMutex(&oldDscp->mx);
! 				cm_dnlcRemove(oldDscp, oldNamep);
! 				code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
! 						 CM_SCACHESYNC_STOREDATA);
! 				lock_ReleaseMutex(&oldDscp->mx);
!                                 if (code) {
! 					/* cleanup first one */
!                                         cm_SyncOpDone(newDscp, NULL,
! 						      CM_SCACHESYNC_STOREDATA);
!                                 }
!                         }
!                 }
!         }	/* two distinct vnodes */
  
!         if (code) {
!         	return code;
! 	}
!         didEnd = 0;
  
! 	/* try the RPC now */
!         do {
! 		code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
!                 if (code) continue;
!                 
! 		oldDirAFSFid.Volume = oldDscp->fid.volume;
!                 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
!                 oldDirAFSFid.Unique = oldDscp->fid.unique;
! 		newDirAFSFid.Volume = newDscp->fid.volume;
!                 newDirAFSFid.Vnode = newDscp->fid.vnode;
!                 newDirAFSFid.Unique = newDscp->fid.unique;
!                 code = RXAFS_Rename(connp->callp, &oldDirAFSFid, oldNamep,
!                 	&newDirAFSFid, newNamep,
! 			&updatedOldDirStatus, &updatedNewDirStatus,
!                 	&volSync);
! 	} while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
! 			    &volSync, NULL, NULL, code));
!         code = cm_MapRPCError(code, reqp);
!         
! 	/* update the individual stat cache entries for the directories */
!         lock_ObtainMutex(&oldDscp->mx);
!         cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
! 	if (code == 0) {
! 	        cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
! 				userp, 0);
! 	}
!         lock_ReleaseMutex(&oldDscp->mx);
  
! 	/* and update it for the new one, too, if necessary */
! 	if (!oneDir) {
! 	        lock_ObtainMutex(&newDscp->mx);
! 	        cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
! 		if (code == 0) {
! 		        cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
! 					userp, 0);
! 		}
! 	        lock_ReleaseMutex(&newDscp->mx);
! 	}
  
!         /* and return error code */
!         return code;
  }
  
  long cm_Lock(cm_scache_t *scp, unsigned char LockType,
! 	LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
! 	u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
! 	void **lockpp)
! {
! 	long code;
! 	int Which = ((LockType & 0x1) ? LockRead : LockWrite);
! 	AFSFid tfid;
! 	AFSVolSync volSync;
! 	cm_conn_t *connp;
! 	cm_file_lock_t *fileLock;
! 	osi_queue_t *q;
! 	int found = 0;
! 
! 	/* Look for a conflict.  Also, if we are asking for a shared lock,
! 	 * look for another shared lock, so we don't have to do an RPC.
! 	 */
! 	q = scp->fileLocks;
! 	while (q) {
! 		fileLock = (cm_file_lock_t *)
! 				((char *) q - offsetof(cm_file_lock_t, fileq));
! 		if ((fileLock->flags &
! 			(CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
! 		    == 0) {
! 			if ((LockType & 0x1) == 0
! 			    || (fileLock->LockType & 0x1) == 0)
! 				return CM_ERROR_WOULDBLOCK;
! 			found = 1;
! 		}
! 		q = osi_QNext(q);
! 	}
! 
! 	if (found)
! 		code = 0;
! 	else {
! 		tfid.Volume = scp->fid.volume;
! 		tfid.Vnode = scp->fid.vnode;
! 		tfid.Unique = scp->fid.unique;
! 		lock_ReleaseMutex(&scp->mx);
! 		do {
! 			code = cm_Conn(&scp->fid, userp, reqp, &connp);
! 			if (code) break;
! 			code = RXAFS_SetLock(connp->callp, &tfid, Which,
! 					     &volSync);
! 		} while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
! 				    NULL, NULL, code));
! 		lock_ObtainMutex(&scp->mx);
! 		code = cm_MapRPCError(code, reqp);
! 	}
! 
! 	if (code == 0 || Timeout != 0) {
! 		fileLock = malloc(sizeof(cm_file_lock_t));
! 		fileLock->LockType = LockType;
! 		cm_HoldUser(userp);
! 		fileLock->userp = userp;
! 		fileLock->fid = scp->fid;
! 		fileLock->LOffset = LOffset;
! 		fileLock->LLength = LLength;
! 		fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
! 		osi_QAdd(&scp->fileLocks, &fileLock->fileq);
! 		lock_ObtainWrite(&cm_scacheLock);
! 		osi_QAdd(&cm_allFileLocks, &fileLock->q);
! 		lock_ReleaseWrite(&cm_scacheLock);
! 		if (code != 0) *lockpp = fileLock;
! 	}
! 	return code;
  }
  
  long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
! 	LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
! 	cm_user_t *userp, cm_req_t *reqp)
  {
! 	long code = 0;
! 	int Which = ((LockType & 0x1) ? LockRead : LockWrite);
! 	AFSFid tfid;
! 	AFSVolSync volSync;
! 	cm_conn_t *connp;
! 	cm_file_lock_t *fileLock, *ourLock;
! 	osi_queue_t *q, *qq;
! 	int anotherReader = 0;
! 	int smallLock = 0;
! 	int found = 0;
! 
! 	if (LargeIntegerLessThan(LLength, scp->length))
! 		smallLock = 1;
! 
! 	/* Look for our own lock on the list, so as to remove it.
! 	 * Also, determine if we're the last reader; if not, avoid an RPC.
! 	 */
! 	q = scp->fileLocks;
! 	while (q) {
! 		fileLock = (cm_file_lock_t *)
              ((char *) q - offsetof(cm_file_lock_t, fileq));
! 		if (!found
! 		    && fileLock->userp == userp
! 		    && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
! 		    && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
! 			found = 1;
! 			ourLock = fileLock;
! 			qq = q;
! 		}
! 		else if (fileLock->LockType & 0x1)
! 			anotherReader = 1;
! 		q = osi_QNext(q);
! 	}
! 
! 	/* ignore byte ranges */
! 	if (smallLock && !found)
! 		return 0;
! 
! 	/* don't try to unlock other people's locks */
! 	if (!found)
! 		return CM_ERROR_WOULDBLOCK;
! 
! 	/* discard lock record */
! 	osi_QRemove(&scp->fileLocks, qq);
! 	/*
! 	 * Don't delete it here; let the daemon delete it, to simplify
! 	 * the daemon's traversal of the list.
! 	 */
! 	lock_ObtainWrite(&cm_scacheLock);
! 	ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
! 	cm_ReleaseUser(ourLock->userp);
! 	lock_ReleaseWrite(&cm_scacheLock);
! 
! 	if (!anotherReader) {
! 		tfid.Volume = scp->fid.volume;
! 		tfid.Vnode = scp->fid.vnode;
! 		tfid.Unique = scp->fid.unique;
! 		lock_ReleaseMutex(&scp->mx);
! 		do {
! 			code = cm_Conn(&scp->fid, userp, reqp, &connp);
! 			if (code) 
                  break;
- 			code = RXAFS_ReleaseLock(connp->callp, &tfid, &volSync);
- 		} while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
- 				    NULL, NULL, code));
- 		code = cm_MapRPCError(code, reqp);
- 		lock_ObtainMutex(&scp->mx);
- 	}
  
! 	return code;
  }
  
  void cm_CheckLocks()
  {
! 	osi_queue_t *q, *nq;
! 	cm_file_lock_t *fileLock;
! 	cm_req_t req;
! 	AFSFid tfid;
! 	AFSVolSync volSync;
! 	cm_conn_t *connp;
! 	long code;
! 
! 	cm_InitReq(&req);
! 
! 	lock_ObtainWrite(&cm_scacheLock);
! 	q = cm_allFileLocks;
! 	while (q) {
! 		fileLock = (cm_file_lock_t *) q;
! 		nq = osi_QNext(q);
! 		if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
! 			osi_QRemove(&cm_allFileLocks, q);
! 			free(fileLock);
! 		}
! 		else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
! 			tfid.Volume = fileLock->fid.volume;
! 			tfid.Vnode = fileLock->fid.vnode;
! 			tfid.Unique = fileLock->fid.unique;
! 			lock_ReleaseWrite(&cm_scacheLock);
! 			do {
! 				code = cm_Conn(&fileLock->fid, fileLock->userp,
! 						&req, &connp);
! 				if (code) break;
! 				code = RXAFS_ExtendLock(connp->callp, &tfid,
! 							&volSync);
! 			} while (cm_Analyze(connp, fileLock->userp, &req,
! 					    &fileLock->fid, &volSync, NULL, NULL,
! 					    code));
! 			code = cm_MapRPCError(code, &req);
! 			lock_ObtainWrite(&cm_scacheLock);
! 		}
! 		q = nq;
! 	}
! 	lock_ReleaseWrite(&cm_scacheLock);
! }
  
  long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
  {
! 	long code;
! 	int Which = ((oldFileLock->LockType & 0x1) ? LockRead : LockWrite);
! 	cm_scache_t *scp;
! 	AFSFid tfid;
! 	AFSVolSync volSync;
! 	cm_conn_t *connp;
! 	cm_file_lock_t *fileLock;
! 	osi_queue_t *q;
! 	cm_req_t req;
! 	int found = 0;
! 
! 	if (vcp_is_dead) {
! 		code = CM_ERROR_TIMEDOUT;
! 		goto handleCode;
! 	}
! 
! 	cm_InitReq(&req);
! 
! 	/* Look for a conflict.  Also, if we are asking for a shared lock,
! 	 * look for another shared lock, so we don't have to do an RPC.
! 	 */
! 	code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
! 	if (code)
! 		return code;
! 
! 	q = scp->fileLocks;
! 	while (q) {
! 		fileLock = (cm_file_lock_t *)
! 				((char *) q - offsetof(cm_file_lock_t, fileq));
! 		if ((fileLock->flags &
! 			(CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
! 		    == 0) {
! 			if ((oldFileLock->LockType & 0x1) == 0
! 			    || (fileLock->LockType & 0x1) == 0) {
! 				cm_ReleaseSCache(scp);
! 				return CM_ERROR_WOULDBLOCK;
! 			}
! 			found = 1;
! 		}
! 		q = osi_QNext(q);
! 	}
! 
! 	if (found)
! 		code = 0;
! 	else {
! 		tfid.Volume = oldFileLock->fid.volume;
! 		tfid.Vnode = oldFileLock->fid.vnode;
! 		tfid.Unique = oldFileLock->fid.unique;
! 		do {
! 			code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
! 				       &req, &connp);
! 			if (code) break;
! 			code = RXAFS_SetLock(connp->callp, &tfid, Which,
! 					     &volSync);
! 		} while (cm_Analyze(connp, oldFileLock->userp, &req,
! 				    &oldFileLock->fid, &volSync,
! 				    NULL, NULL, code));
! 		code = cm_MapRPCError(code, &req);
! 	}
  
    handleCode:
! 	if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
! 		lock_ObtainMutex(&scp->mx);
! 		osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
! 		lock_ReleaseMutex(&scp->mx);
! 	}
! 	lock_ObtainWrite(&cm_scacheLock);
! 	if (code == 0)
! 		oldFileLock->flags = 0;
! 	else if (code != CM_ERROR_WOULDBLOCK) {
! 		oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
! 		cm_ReleaseUser(oldFileLock->userp);
          oldFileLock->userp = NULL;
! 	}
! 	lock_ReleaseWrite(&cm_scacheLock);
  
! 	return code;
  }
--- 1554,2810 ----
   * processing, to avoid deadlocks.
   */
  long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
!                      osi_hyper_t *offp)
  {
!     osi_hyper_t thyper;
!     cm_bulkStat_t *bsp;
!     int i;
!     cm_scache_t *tscp;
!     cm_fid_t tfid;
  
!     bsp = rockp;
  
!     /* Don't overflow bsp. */
!     if (bsp->counter >= CM_BULKMAX)
!         return CM_ERROR_STOPNOW;
  
!     thyper.LowPart = buf_bufferSize;
!     thyper.HighPart = 0;
!     thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
! 
!     /* thyper is now the first byte past the end of the record we're
!      * interested in, and bsp->bufOffset is the first byte of the record
!      * we're interested in.
!      * Skip data in the others.
!      * Skip '.' and '..'
!      */
!     if (LargeIntegerLessThan(*offp, bsp->bufOffset))
!         return 0;
!     if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
!         return CM_ERROR_STOPNOW;
!     if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
!         return 0;
! 
!     tfid.cell = scp->fid.cell;
!     tfid.volume = scp->fid.volume;
!     tfid.vnode = ntohl(dep->fid.vnode);
!     tfid.unique = ntohl(dep->fid.unique);
!     tscp = cm_FindSCache(&tfid);
!     if (tscp) {
!         if (lock_TryMutex(&tscp->mx)) {
!             /* we have an entry that we can look at */
!             if (cm_HaveCallback(tscp)) {
!                 /* we have a callback on it.  Don't bother
!                  * fetching this stat entry, since we're happy
!                  * with the info we have.
!                  */
!                 lock_ReleaseMutex(&tscp->mx);
                  cm_ReleaseSCache(tscp);
!                 return 0;
!             }
!             lock_ReleaseMutex(&tscp->mx);
!         }	/* got lock */
!         cm_ReleaseSCache(tscp);
!     }	/* found entry */
  
  #ifdef AFS_FREELANCE_CLIENT
!     // yj: if this is a mountpoint under root.afs then we don't want it
!     // to be bulkstat-ed, instead, we call getSCache directly and under
!     // getSCache, it is handled specially.
!     if 	( cm_freelanceEnabled &&
            tfid.cell==AFS_FAKE_ROOT_CELL_ID && 
            tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
            !(tfid.vnode==0x1 && tfid.unique==0x1) )
!     {       
          osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
!         return cm_GetSCache(&tfid, &tscp, NULL, NULL);
!     }
  #endif /* AFS_FREELANCE_CLIENT */
  
!     i = bsp->counter++;
!     bsp->fids[i].Volume = scp->fid.volume;
!     bsp->fids[i].Vnode = tfid.vnode;
!     bsp->fids[i].Unique = tfid.unique;
!     return 0;
! }       
  
  /* called with a locked scp and a pointer to a buffer.  Make bulk stat
   * calls on all undeleted files in the page of the directory specified.
   */
  void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
!                      cm_req_t *reqp)
  {
!     long code;
!     cm_bulkStat_t bb;	/* this is *BIG*, probably 12K or so;
!                          * watch for stack problems */
!     AFSCBFids fidStruct;
!     AFSBulkStats statStruct;
!     cm_conn_t *connp;
!     AFSCBs callbackStruct;
!     long filex;
!     AFSVolSync volSync;
!     cm_callbackRequest_t cbReq;
!     long filesThisCall;
!     long i;
!     long j;
!     cm_scache_t *scp;
!     cm_fid_t tfid;
!     struct rx_connection * callp;
! 
!     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
! 
!     /* should be on a buffer boundary */
!     osi_assert((offsetp->LowPart & (buf_bufferSize - 1)) == 0);
! 
!     bb.counter = 0;
!     bb.bufOffset = *offsetp;
! 
!     /* first, assemble the file IDs we need to stat */
!     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp,
!                         reqp, NULL);
! 
!     /* if we failed, bail out early */
!     if (code && code != CM_ERROR_STOPNOW) return;
! 
!     /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
!      * make the calls to create the entries.  Handle AFSCBMAX files at a
!      * time.
!      */
!     filex = 0;
!     while(filex < bb.counter) {
!         filesThisCall = bb.counter - filex;
!         if (filesThisCall > AFSCBMAX) filesThisCall = AFSCBMAX;
! 
!         fidStruct.AFSCBFids_len = filesThisCall;
!         fidStruct.AFSCBFids_val = &bb.fids[filex];
!         statStruct.AFSBulkStats_len = filesThisCall;
!         statStruct.AFSBulkStats_val = &bb.stats[filex];
!         callbackStruct.AFSCBs_len = filesThisCall;
!         callbackStruct.AFSCBs_val = &bb.callbacks[filex];
!         cm_StartCallbackGrantingCall(NULL, &cbReq);
!         osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
!         do {
!             code = cm_Conn(&dscp->fid, userp, reqp, &connp);
!             if (code) 
!                 continue;
! 
!             callp = cm_GetRxConn(connp);
!             code = RXAFS_BulkStatus(callp, &fidStruct,
!                                      &statStruct, &callbackStruct, &volSync);
!             rx_PutConnection(callp);
  
!         } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
!                              &volSync, NULL, &cbReq, code));
!         code = cm_MapRPCError(code, reqp);
! 
!         osi_Log0(afsd_logp, "CALL BulkStatus DONE");
! 
!         /* may as well quit on an error, since we're not going to do
!          * much better on the next immediate call, either.
           */
!         if (code) 
!             break;
  
!         /* otherwise, we should do the merges */
!         for(i = 0; i<filesThisCall; i++) {
!             j = filex + i;
!             tfid.cell = dscp->fid.cell;
!             tfid.volume = bb.fids[j].Volume;
!             tfid.vnode = bb.fids[j].Vnode;
!             tfid.unique = bb.fids[j].Unique;
!             code = cm_GetSCache(&tfid, &scp, userp, reqp);
!             if (code != 0) 
!                 continue;
  
!             /* otherwise, if this entry has no callback info, 
!              * merge in this.
!              */
!             lock_ObtainMutex(&scp->mx);
!             /* now, we have to be extra paranoid on merging in this
!              * information, since we didn't use cm_SyncOp before
!              * starting the fetch to make sure that no bad races
!              * were occurring.  Specifically, we need to make sure
!              * we don't obliterate any newer information in the
!              * vnode than have here.
!              *
!              * Right now, be pretty conservative: if there's a
!              * callback or a pending call, skip it.
!              */
!             if (scp->cbServerp == NULL
!                  && !(scp->flags &
!                        (CM_SCACHEFLAG_FETCHING
!                          | CM_SCACHEFLAG_STORING
!                          | CM_SCACHEFLAG_SIZESTORING))) {
!                 cm_EndCallbackGrantingCall(scp, &cbReq,
!                                             &bb.callbacks[j],
!                                             CM_CALLBACK_MAINTAINCOUNT);
!                 cm_MergeStatus(scp, &bb.stats[j], &volSync,
!                                 userp, 0);
!             }       
!             lock_ReleaseMutex(&scp->mx);
!             cm_ReleaseSCache(scp);
!         } /* all files in the response */
!         /* now tell it to drop the count,
!          * after doing the vnode processing above */
          cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
! 
!         filex += filesThisCall;
!     }	/* while there are still more files to process */
!     osi_Log0(afsd_logp, "END cm_TryBulkStat");
! }       
  
  void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
  {
!     long mask;
  
!     /* initialize store back mask as inexpensive local variable */
!     mask = 0;
!     memset(statusp, 0, sizeof(AFSStoreStatus));
! 
!     /* copy out queued info from scache first, if scp passed in */
!     if (scp) {
!         if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
!             statusp->ClientModTime = scp->clientModTime;
!             mask |= AFS_SETMODTIME;
!             scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
!         }
!     }
! 
!     if (attrp) {
!         /* now add in our locally generated request */
!         if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
!             statusp->ClientModTime = attrp->clientModTime;
!             mask |= AFS_SETMODTIME;
!         }
!         if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
!             statusp->UnixModeBits = attrp->unixModeBits;
!             mask |= AFS_SETMODE;
!         }
!         if (attrp->mask & CM_ATTRMASK_OWNER) {
!             statusp->Owner = attrp->owner;
!             mask |= AFS_SETOWNER;
!         }
!         if (attrp->mask & CM_ATTRMASK_GROUP) {
!             statusp->Group = attrp->group;
!             mask |= AFS_SETGROUP;
!         }
!     }
!     statusp->Mask = mask;
! }       
  
  /* set the file size, and make sure that all relevant buffers have been
   * truncated.  Ensure that any partially truncated buffers have been zeroed
   * to the end of the buffer.
   */
  long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
!                    cm_req_t *reqp)
  {
!     long code;
!     int shrinking;
  
!     /* start by locking out buffer creation */
!     lock_ObtainWrite(&scp->bufCreateLock);
  
!     /* verify that this is a file, not a dir or a symlink */
!     lock_ObtainMutex(&scp->mx);
!     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
!                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
!     if (code) 
!         goto done;
!         
!     if (scp->fileType != CM_SCACHETYPE_FILE) {
!         code = CM_ERROR_ISDIR;
!         goto done;
!     }
  
!   startover:
!     if (LargeIntegerLessThan(*sizep, scp->length))
!         shrinking = 1;
!     else
!         shrinking = 0;
! 
!     lock_ReleaseMutex(&scp->mx);
! 
!     /* can't hold scp->mx lock here, since we may wait for a storeback to
!      * finish if the buffer package is cleaning a buffer by storing it to
!      * the server.
!      */
!     if (shrinking)
!         buf_Truncate(scp, userp, reqp, sizep);
  
!     /* now ensure that file length is short enough, and update truncPos */
!     lock_ObtainMutex(&scp->mx);
  
!     /* make sure we have a callback (so we have the right value for the
!      * length), and wait for it to be safe to do a truncate.
!      */
!     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
!                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
!                       | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
!     if (code) 
!         goto done;
! 
!     if (LargeIntegerLessThan(*sizep, scp->length)) {
!         /* a real truncation.  If truncPos is not set yet, or is bigger
!          * than where we're truncating the file, set truncPos to this
!          * new value.
!          */
!         if (!shrinking)
!             goto startover;
!         if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
!              || LargeIntegerLessThan(*sizep, scp->length)) {
!             /* set trunc pos */
!             scp->truncPos = *sizep;
!             scp->mask |= CM_SCACHEMASK_TRUNCPOS;
!         }
!         /* in either case, the new file size has been changed */
!         scp->length = *sizep;
!         scp->mask |= CM_SCACHEMASK_LENGTH;
!     }
!     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
!         /* really extending the file */
!         scp->length = *sizep;
!         scp->mask |= CM_SCACHEMASK_LENGTH;
!     }
  
!     /* done successfully */
!     code = 0;
  
!   done:
!     lock_ReleaseMutex(&scp->mx);
!     lock_ReleaseWrite(&scp->bufCreateLock);
  
!     return code;
  }
  
  /* set the file size or other attributes (but not both at once) */
  long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
!                 cm_req_t *reqp)
  {
!     long code;
!     int flags;
!     AFSFetchStatus afsOutStatus;
!     AFSVolSync volSync;
!     cm_conn_t *connp;
!     AFSFid tfid;
!     AFSStoreStatus afsInStatus;
!     struct rx_connection * callp;
! 
!     /* handle file length setting */
!     if (attrp->mask & CM_ATTRMASK_LENGTH)
!         return cm_SetLength(scp, &attrp->length, userp, reqp);
! 
!     flags = CM_SCACHESYNC_STORESTATUS;
! 
!     lock_ObtainMutex(&scp->mx);
!     /* otherwise, we have to make an RPC to get the status */
!     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
  
!     /* make the attr structure */
!     cm_StatusFromAttr(&afsInStatus, scp, attrp);
  
!     lock_ReleaseMutex(&scp->mx);
!     if (code) 
!         return code;
  
!     /* now make the RPC */
!     osi_Log1(afsd_logp, "CALL StoreStatus vp %x", (long) scp);
!     tfid.Volume = scp->fid.volume;
!     tfid.Vnode = scp->fid.vnode;
!     tfid.Unique = scp->fid.unique;
!     do {
!         code = cm_Conn(&scp->fid, userp, reqp, &connp);
!         if (code) 
!             continue;
  
!         callp = cm_GetRxConn(connp);
!         code = RXAFS_StoreStatus(callp, &tfid,
!                                   &afsInStatus, &afsOutStatus, &volSync);
!         rx_PutConnection(callp);
! 
!     } while (cm_Analyze(connp, userp, reqp,
!                          &scp->fid, &volSync, NULL, NULL, code));
!     code = cm_MapRPCError(code, reqp);
! 
!     osi_Log1(afsd_logp, "CALL StoreStatus DONE, code %d", code);
! 
!     lock_ObtainMutex(&scp->mx);
!     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
!     if (code == 0)
!         cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
!                         CM_MERGEFLAG_FORCE);
! 	
!     /* if we're changing the mode bits, discard the ACL cache, 
!      * since we changed the mode bits.
!      */
!     if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
!     lock_ReleaseMutex(&scp->mx);
!     return code;
! }       
  
! long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
!                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
! {       
!     cm_conn_t *connp;
!     long code;
!     AFSFid dirAFSFid;
!     cm_callbackRequest_t cbReq;
!     AFSFid newAFSFid;
!     cm_fid_t newFid;
!     cm_scache_t *scp;
!     int didEnd;
!     AFSStoreStatus inStatus;
!     AFSFetchStatus updatedDirStatus;
!     AFSFetchStatus newFileStatus;
!     AFSCallBack newFileCallback;
!     AFSVolSync volSync;
!     struct rx_connection * callp;
  
!     /* can't create names with @sys in them; must expand it manually first.
!      * return "invalid request" if they try.
!      */
!     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
!         return CM_ERROR_ATSYS;
!     }
  
!     /* before starting the RPC, mark that we're changing the file data, so
!      * that someone who does a chmod will know to wait until our call
!      * completes.
!      */
!     lock_ObtainMutex(&dscp->mx);
!     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
!     if (code == 0) {
!         cm_StartCallbackGrantingCall(NULL, &cbReq);
!     }
!     lock_ReleaseMutex(&dscp->mx);
!     if (code) {
          return code;
!     }
!     didEnd = 0;
  
!     cm_StatusFromAttr(&inStatus, NULL, attrp);
  
!     /* try the RPC now */
!     do {
!         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
!         if (code) 
!             continue;
  
!         dirAFSFid.Volume = dscp->fid.volume;
!         dirAFSFid.Vnode = dscp->fid.vnode;
!         dirAFSFid.Unique = dscp->fid.unique;
! 
!         callp = cm_GetRxConn(connp);
!         code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
!                                  &inStatus, &newAFSFid, &newFileStatus,
!                                  &updatedDirStatus, &newFileCallback,
!                                  &volSync);
!         rx_PutConnection(callp);
! 
!     } while (cm_Analyze(connp, userp, reqp,
!                          &dscp->fid, &volSync, NULL, &cbReq, code));
!     code = cm_MapRPCError(code, reqp);
!         
!     lock_ObtainMutex(&dscp->mx);
!     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
!     if (code == 0) {
!         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
!     }
!     lock_ReleaseMutex(&dscp->mx);
! 
!     /* now try to create the file's entry, too, but be careful to 
!      * make sure that we don't merge in old info.  Since we weren't locking
!      * out any requests during the file's creation, we may have pretty old
!      * info.
!      */
!     if (code == 0) {
!         newFid.cell = dscp->fid.cell;
!         newFid.volume = dscp->fid.volume;
!         newFid.vnode = newAFSFid.Vnode;
!         newFid.unique = newAFSFid.Unique;
!         code = cm_GetSCache(&newFid, &scp, userp, reqp);
          if (code == 0) {
!             lock_ObtainMutex(&scp->mx);
!             if (!cm_HaveCallback(scp)) {
!                 cm_MergeStatus(scp, &newFileStatus, &volSync,
!                                 userp, 0);
!                 cm_EndCallbackGrantingCall(scp, &cbReq,
!                                             &newFileCallback, 0);
!                 didEnd = 1;     
!             }       
!             lock_ReleaseMutex(&scp->mx);
!             *scpp = scp;
          }
!     }
  
!     /* make sure we end things properly */
!     if (!didEnd)
!         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
  
!     return code;
! }       
  
  long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
  {
!     long code;
  
!     lock_ObtainWrite(&scp->bufCreateLock);
!     code = buf_CleanVnode(scp, userp, reqp);
!     lock_ReleaseWrite(&scp->bufCreateLock);
!     if (code == 0) {
!         lock_ObtainMutex(&scp->mx);
!         scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
!                          | CM_SCACHEFLAG_OUTOFSPACE);
!         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
!                           | CM_SCACHEMASK_CLIENTMODTIME
!                           | CM_SCACHEMASK_LENGTH))
!             code = cm_StoreMini(scp, userp, reqp);
!         lock_ReleaseMutex(&scp->mx);
!     }
!     return code;
  }
  
  long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
!                  cm_user_t *userp, cm_req_t *reqp)
  {
!     cm_conn_t *connp;
!     long code;
!     AFSFid dirAFSFid;
!     cm_callbackRequest_t cbReq;
!     AFSFid newAFSFid;
!     cm_fid_t newFid;
!     cm_scache_t *scp;
!     int didEnd;
!     AFSStoreStatus inStatus;
!     AFSFetchStatus updatedDirStatus;
!     AFSFetchStatus newDirStatus;
!     AFSCallBack newDirCallback;
!     AFSVolSync volSync;
!     struct rx_connection * callp;
  
!     /* can't create names with @sys in them; must expand it manually first.
!      * return "invalid request" if they try.
!      */
!     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
!         return CM_ERROR_ATSYS;
!     }
  
!     /* before starting the RPC, mark that we're changing the directory
!      * data, so that someone who does a chmod on the dir will wait until
!      * our call completes.
!      */
!     lock_ObtainMutex(&dscp->mx);
!     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
!     if (code == 0) {
!         cm_StartCallbackGrantingCall(NULL, &cbReq);
!     }
!     lock_ReleaseMutex(&dscp->mx);
!     if (code) {
!         return code;
!     }
!     didEnd = 0;
! 
!     cm_StatusFromAttr(&inStatus, NULL, attrp);
! 
!     /* try the RPC now */
!     do {
!         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
!         if (code) 
!             continue;
! 
!         dirAFSFid.Volume = dscp->fid.volume;
!         dirAFSFid.Vnode = dscp->fid.vnode;
!         dirAFSFid.Unique = dscp->fid.unique;
! 
!         callp = cm_GetRxConn(connp);
!         code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
!                               &inStatus, &newAFSFid, &newDirStatus,
!                               &updatedDirStatus, &newDirCallback,
!                               &volSync);
!         rx_PutConnection(callp);
! 
!     } while (cm_Analyze(connp, userp, reqp,
!                          &dscp->fid, &volSync, NULL, &cbReq, code));
!     code = cm_MapRPCError(code, reqp);
!         
!     lock_ObtainMutex(&dscp->mx);
!     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
!     if (code == 0) {
!         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
!     }
!     lock_ReleaseMutex(&dscp->mx);
! 
!     /* now try to create the new dir's entry, too, but be careful to 
!      * make sure that we don't merge in old info.  Since we weren't locking
!      * out any requests during the file's creation, we may have pretty old
!      * info.
!      */
!     if (code == 0) {
!         newFid.cell = dscp->fid.cell;
!         newFid.volume = dscp->fid.volume;
!         newFid.vnode = newAFSFid.Vnode;
!         newFid.unique = newAFSFid.Unique;
!         code = cm_GetSCache(&newFid, &scp, userp, reqp);
          if (code == 0) {
!             lock_ObtainMutex(&scp->mx);
!             if (!cm_HaveCallback(scp)) {
!                 cm_MergeStatus(scp, &newDirStatus, &volSync,
!                                 userp, 0);
!                 cm_EndCallbackGrantingCall(scp, &cbReq,
!                                             &newDirCallback, 0);
!                 didEnd = 1;             
!             }
!             lock_ReleaseMutex(&scp->mx);
!             cm_ReleaseSCache(scp);
          }
!     }
  
!     /* make sure we end things properly */
!     if (!didEnd)
!         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
  
!     /* and return error code */
!     return code;
! }       
  
! long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
!              cm_user_t *userp, cm_req_t *reqp)
! {
!     cm_conn_t *connp;
!     long code = 0;
!     AFSFid dirAFSFid;
!     AFSFid existingAFSFid;
!     AFSFetchStatus updatedDirStatus;
!     AFSFetchStatus newLinkStatus;
!     AFSVolSync volSync;
!     struct rx_connection * callp;
! 
!     if (dscp->fid.cell != sscp->fid.cell ||
!         dscp->fid.volume != sscp->fid.volume) {
!         return CM_ERROR_CROSSDEVLINK;
!     }
! 
!     lock_ObtainMutex(&dscp->mx);
!     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
!     lock_ReleaseMutex(&dscp->mx);
! 
!     if (code)
          return code;
+ 
+     do {
+         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
+         if (code) continue;
+ 
+         dirAFSFid.Volume = dscp->fid.volume;
+         dirAFSFid.Vnode = dscp->fid.vnode;
+         dirAFSFid.Unique = dscp->fid.unique;
+ 
+         existingAFSFid.Volume = sscp->fid.volume;
+         existingAFSFid.Vnode = sscp->fid.vnode;
+         existingAFSFid.Unique = sscp->fid.unique;
+ 
+         callp = cm_GetRxConn(connp);
+         code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
+             &newLinkStatus, &updatedDirStatus, &volSync);
+         rx_PutConnection(callp);
+         osi_Log1(smb_logp,"  RXAFS_Link returns %d", code);
+ 
+     } while (cm_Analyze(connp, userp, reqp,
+         &dscp->fid, &volSync, NULL, NULL, code));
+ 
+     code = cm_MapRPCError(code, reqp);
+ 
+     lock_ObtainMutex(&dscp->mx);
+     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
+     if (code == 0) {
+         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
+     }
+     lock_ReleaseMutex(&dscp->mx);
+ 
+     return code;
  }
  
  long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
!                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
  {
!     cm_conn_t *connp;
!     long code;
!     AFSFid dirAFSFid;
!     AFSFid newAFSFid;
!     cm_fid_t newFid;
!     cm_scache_t *scp;
!     AFSStoreStatus inStatus;
!     AFSFetchStatus updatedDirStatus;
!     AFSFetchStatus newLinkStatus;
!     AFSVolSync volSync;
!     struct rx_connection * callp;
! 
!     /* before starting the RPC, mark that we're changing the directory data,
!      * so that someone who does a chmod on the dir will wait until our
!      * call completes.
!      */
!     lock_ObtainMutex(&dscp->mx);
!     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
!     lock_ReleaseMutex(&dscp->mx);
!     if (code) {
!         return code;
!     }
  
!     cm_StatusFromAttr(&inStatus, NULL, attrp);
  
!     /* try the RPC now */
!     do {
!         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
!         if (code) 
!             continue;
  
!         dirAFSFid.Volume = dscp->fid.volume;
!         dirAFSFid.Vnode = dscp->fid.vnode;
!         dirAFSFid.Unique = dscp->fid.unique;
! 
!         callp = cm_GetRxConn(connp);
!         code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
!                               &inStatus, &newAFSFid, &newLinkStatus,
!                               &updatedDirStatus, &volSync);
!         rx_PutConnection(callp);
! 
!     } while (cm_Analyze(connp, userp, reqp,
!                          &dscp->fid, &volSync, NULL, NULL, code));
!     code = cm_MapRPCError(code, reqp);
!         
!     lock_ObtainMutex(&dscp->mx);
!     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
!     if (code == 0) {
!         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
!     }
!     lock_ReleaseMutex(&dscp->mx);
! 
!     /* now try to create the new dir's entry, too, but be careful to 
!      * make sure that we don't merge in old info.  Since we weren't locking
!      * out any requests during the file's creation, we may have pretty old
!      * info.
!      */
!     if (code == 0) {
!         newFid.cell = dscp->fid.cell;
!         newFid.volume = dscp->fid.volume;
!         newFid.vnode = newAFSFid.Vnode;
!         newFid.unique = newAFSFid.Unique;
!         code = cm_GetSCache(&newFid, &scp, userp, reqp);
!         if (code == 0) {
!             lock_ObtainMutex(&scp->mx);
!             if (!cm_HaveCallback(scp)) {
!                 cm_MergeStatus(scp, &newLinkStatus, &volSync,
!                                 userp, 0);
!             }       
!             lock_ReleaseMutex(&scp->mx);
!             cm_ReleaseSCache(scp);
          }
+     }
  	
!     /* and return error code */
!     return code;
  }
  
  long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
!                    cm_req_t *reqp)
  {
!     cm_conn_t *connp;
!     long code;
!     AFSFid dirAFSFid;
!     int didEnd;
!     AFSFetchStatus updatedDirStatus;
!     AFSVolSync volSync;
!     struct rx_connection * callp;
! 
!     /* before starting the RPC, mark that we're changing the directory data,
!      * so that someone who does a chmod on the dir will wait until our
!      * call completes.
!      */
!     lock_ObtainMutex(&dscp->mx);
!     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
!     lock_ReleaseMutex(&dscp->mx);
!     if (code) {
!         return code;
!     }
!     didEnd = 0;
  
!     /* try the RPC now */
!     do {
!         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
!         if (code) 
!             continue;
  
!         dirAFSFid.Volume = dscp->fid.volume;
!         dirAFSFid.Vnode = dscp->fid.vnode;
!         dirAFSFid.Unique = dscp->fid.unique;
! 
!         callp = cm_GetRxConn(connp);
!         code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
!                                 &updatedDirStatus, &volSync);
!         rx_PutConnection(callp);
! 
!     } while (cm_Analyze(connp, userp, reqp,
!                          &dscp->fid, &volSync, NULL, NULL, code));
!     code = cm_MapRPCErrorRmdir(code, reqp);
!         
!     lock_ObtainMutex(&dscp->mx);
!     cm_dnlcRemove(dscp, namep); 
!     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
!     if (code == 0) {
!         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
!     }
!     lock_ReleaseMutex(&dscp->mx);
! 
!     /* and return error code */
!     return code;
  }
  
  long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
  {
!     /* grab mutex on contents */
!     lock_ObtainMutex(&scp->mx);
  
!     /* reset the prefetch info */
!     scp->prefetch.base.LowPart = 0;		/* base */
!     scp->prefetch.base.HighPart = 0;
!     scp->prefetch.end.LowPart = 0;		/* and end */
!     scp->prefetch.end.HighPart = 0;
! 
!     /* release mutex on contents */
!     lock_ReleaseMutex(&scp->mx);
! 
!     /* we're done */
!     return 0;
! }       
  
  long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
!                 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
  {
!     cm_conn_t *connp;
!     long code;
!     AFSFid oldDirAFSFid;
!     AFSFid newDirAFSFid;
!     int didEnd;
!     AFSFetchStatus updatedOldDirStatus;
!     AFSFetchStatus updatedNewDirStatus;
!     AFSVolSync volSync;
!     int oneDir;
!     struct rx_connection * callp;
! 
!     /* before starting the RPC, mark that we're changing the directory data,
!      * so that someone who does a chmod on the dir will wait until our call
!      * completes.  We do this in vnode order so that we don't deadlock,
!      * which makes the code a little verbose.
!      */
!     if (oldDscp == newDscp) {
!         /* check for identical names */
!         if (strcmp(oldNamep, newNamep) == 0)
!             return CM_ERROR_RENAME_IDENTICAL;
! 
!         oneDir = 1;
!         lock_ObtainMutex(&oldDscp->mx);
!         cm_dnlcRemove(oldDscp, oldNamep);
!         cm_dnlcRemove(oldDscp, newNamep);
!         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
!                           CM_SCACHESYNC_STOREDATA);
!         lock_ReleaseMutex(&oldDscp->mx);
!     }
!     else {
!         /* two distinct dir vnodes */
!         oneDir = 0;
!         if (oldDscp->fid.cell != newDscp->fid.cell ||
!              oldDscp->fid.volume != newDscp->fid.volume)
!             return CM_ERROR_CROSSDEVLINK;
! 
!         /* shouldn't happen that we have distinct vnodes for two
!          * different files, but could due to deliberate attack, or
!          * stale info.  Avoid deadlocks and quit now.
!          */
!         if (oldDscp->fid.vnode == newDscp->fid.vnode)
!             return CM_ERROR_CROSSDEVLINK;
! 
!         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
!             lock_ObtainMutex(&oldDscp->mx);
!             cm_dnlcRemove(oldDscp, oldNamep);
!             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
!                               CM_SCACHESYNC_STOREDATA);
!             lock_ReleaseMutex(&oldDscp->mx);
!             if (code == 0) {
!                 lock_ObtainMutex(&newDscp->mx);
!                 cm_dnlcRemove(newDscp, newNamep);
!                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
!                                   CM_SCACHESYNC_STOREDATA);
!                 lock_ReleaseMutex(&newDscp->mx);
!                 if (code) {
!                     /* cleanup first one */
!                     cm_SyncOpDone(oldDscp, NULL,
!                                    CM_SCACHESYNC_STOREDATA);
!                 }       
!             }
          }
          else {
!             /* lock the new vnode entry first */
!             lock_ObtainMutex(&newDscp->mx);
!             cm_dnlcRemove(newDscp, newNamep);
!             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
!                               CM_SCACHESYNC_STOREDATA);
!             lock_ReleaseMutex(&newDscp->mx);
!             if (code == 0) {
!                 lock_ObtainMutex(&oldDscp->mx);
!                 cm_dnlcRemove(oldDscp, oldNamep);
!                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
!                                   CM_SCACHESYNC_STOREDATA);
!                 lock_ReleaseMutex(&oldDscp->mx);
!                 if (code) {
!                     /* cleanup first one */
!                     cm_SyncOpDone(newDscp, NULL,
!                                    CM_SCACHESYNC_STOREDATA);
!                 }       
!             }
!         }
!     }	/* two distinct vnodes */
  
!     if (code) {
!         return code;
!     }
!     didEnd = 0;
  
!     /* try the RPC now */
!     do {
!         code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
!         if (code) 
!             continue;
  
!         oldDirAFSFid.Volume = oldDscp->fid.volume;
!         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
!         oldDirAFSFid.Unique = oldDscp->fid.unique;
!         newDirAFSFid.Volume = newDscp->fid.volume;
!         newDirAFSFid.Vnode = newDscp->fid.vnode;
!         newDirAFSFid.Unique = newDscp->fid.unique;
! 
!         callp = cm_GetRxConn(connp);
!         code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
!                              &newDirAFSFid, newNamep,
!                              &updatedOldDirStatus, &updatedNewDirStatus,
!                              &volSync);
!         rx_PutConnection(callp);
! 
!     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
!                          &volSync, NULL, NULL, code));
!     code = cm_MapRPCError(code, reqp);
!         
!     /* update the individual stat cache entries for the directories */
!     lock_ObtainMutex(&oldDscp->mx);
!     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
!     if (code == 0) {
!         cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
!                         userp, 0);
!     }
!     lock_ReleaseMutex(&oldDscp->mx);
  
!     /* and update it for the new one, too, if necessary */
!     if (!oneDir) {
!         lock_ObtainMutex(&newDscp->mx);
!         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
!         if (code == 0) {
!             cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
!                             userp, 0);
!         }
!         lock_ReleaseMutex(&newDscp->mx);
!     }
! 
!     /* and return error code */
!     return code;
  }
  
  long cm_Lock(cm_scache_t *scp, unsigned char LockType,
!               LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
!               u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
!               void **lockpp)
! {
!     long code;
!     int Which = ((LockType & 0x1) ? LockRead : LockWrite);
!     AFSFid tfid;
!     AFSVolSync volSync;
!     cm_conn_t *connp;
!     cm_file_lock_t *fileLock;
!     osi_queue_t *q;
!     int found = 0;
!     struct rx_connection * callp;
! 
!     /* Look for a conflict.  Also, if we are asking for a shared lock,
!      * look for another shared lock, so we don't have to do an RPC.
!      */
!     q = scp->fileLocks;
!     while (q) {
!         fileLock = (cm_file_lock_t *)
!             ((char *) q - offsetof(cm_file_lock_t, fileq));
!         if ((fileLock->flags &
!               (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
!              == 0) {
!             if ((LockType & 0x1) == 0
!                  || (fileLock->LockType & 0x1) == 0)
!                 return CM_ERROR_WOULDBLOCK;
!             found = 1;
!         }
!         q = osi_QNext(q);
!     }
! 
!     if (found)
!         code = 0;
!     else {
!         tfid.Volume = scp->fid.volume;
!         tfid.Vnode = scp->fid.vnode;
!         tfid.Unique = scp->fid.unique;
!         lock_ReleaseMutex(&scp->mx);
!         do {
!             code = cm_Conn(&scp->fid, userp, reqp, &connp);
!             if (code) 
!                 break;
! 
!             callp = cm_GetRxConn(connp);
!             code = RXAFS_SetLock(callp, &tfid, Which,
!                                   &volSync);
!             rx_PutConnection(callp);
! 
!         } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
!                              NULL, NULL, code));
!         lock_ObtainMutex(&scp->mx);
!         code = cm_MapRPCError(code, reqp);
!     }
! 
!     if (code == 0 || Timeout != 0) {
!         fileLock = malloc(sizeof(cm_file_lock_t));
!         fileLock->LockType = LockType;
!         cm_HoldUser(userp);
!         fileLock->userp = userp;
!         fileLock->fid = scp->fid;
!         fileLock->LOffset = LOffset;
!         fileLock->LLength = LLength;
!         fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
!         osi_QAdd(&scp->fileLocks, &fileLock->fileq);
!         lock_ObtainWrite(&cm_scacheLock);
!         osi_QAdd(&cm_allFileLocks, &fileLock->q);
!         lock_ReleaseWrite(&cm_scacheLock);
!         if (code != 0) 
!             *lockpp = fileLock;
!     }
!     return code;
  }
  
  long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
!                 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
!                 cm_user_t *userp, cm_req_t *reqp)
  {
!     long code = 0;
!     int Which = ((LockType & 0x1) ? LockRead : LockWrite);
!     AFSFid tfid;
!     AFSVolSync volSync;
!     cm_conn_t *connp;
!     cm_file_lock_t *fileLock, *ourLock;
!     osi_queue_t *q, *qq;
!     int anotherReader = 0;
!     int smallLock = 0;
!     int found = 0;
!     struct rx_connection * callp;
! 
!     if (LargeIntegerLessThan(LLength, scp->length))
!         smallLock = 1;
! 
!     /* Look for our own lock on the list, so as to remove it.
!      * Also, determine if we're the last reader; if not, avoid an RPC.
!      */
!     q = scp->fileLocks;
!     while (q) {
!         fileLock = (cm_file_lock_t *)
              ((char *) q - offsetof(cm_file_lock_t, fileq));
!         if (!found
!              && fileLock->userp == userp
!              && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
!              && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
!             found = 1;
!             ourLock = fileLock;
!             qq = q;
!         }
!         else if (fileLock->LockType & 0x1)
!             anotherReader = 1;
!         q = osi_QNext(q);
!     }
! 
!     /* ignore byte ranges */
!     if (smallLock && !found)
!         return 0;
! 
!     /* don't try to unlock other people's locks */
!     if (!found)
!         return CM_ERROR_WOULDBLOCK;
! 
!     /* discard lock record */
!     osi_QRemove(&scp->fileLocks, qq);
!     /*
!      * Don't delete it here; let the daemon delete it, to simplify
!      * the daemon's traversal of the list.
!      */
!     lock_ObtainWrite(&cm_scacheLock);
!     ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
!     cm_ReleaseUser(ourLock->userp);
!     lock_ReleaseWrite(&cm_scacheLock);
! 
!     if (!anotherReader) {
!         tfid.Volume = scp->fid.volume;
!         tfid.Vnode = scp->fid.vnode;
!         tfid.Unique = scp->fid.unique;
!         lock_ReleaseMutex(&scp->mx);
!         do {
!             code = cm_Conn(&scp->fid, userp, reqp, &connp);
!             if (code) 
                  break;
  
!             callp = cm_GetRxConn(connp);
!             code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
!             rx_PutConnection(callp);
! 
!         } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
!                              NULL, NULL, code));
!         code = cm_MapRPCError(code, reqp);
!         lock_ObtainMutex(&scp->mx);
!     }
! 
!     return code;
  }
  
  void cm_CheckLocks()
  {
!     osi_queue_t *q, *nq;
!     cm_file_lock_t *fileLock;
!     cm_req_t req;
!     AFSFid tfid;
!     AFSVolSync volSync;
!     cm_conn_t *connp;
!     long code;
!     struct rx_connection * callp;
! 
!     cm_InitReq(&req);
! 
!     lock_ObtainWrite(&cm_scacheLock);
!     q = cm_allFileLocks;
!     while (q) {
!         fileLock = (cm_file_lock_t *) q;
!         nq = osi_QNext(q);
!         if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
!             osi_QRemove(&cm_allFileLocks, q);
!             free(fileLock);
!         }
!         else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
!             tfid.Volume = fileLock->fid.volume;
!             tfid.Vnode = fileLock->fid.vnode;
!             tfid.Unique = fileLock->fid.unique;
!             lock_ReleaseWrite(&cm_scacheLock);
!             do {
!                 code = cm_Conn(&fileLock->fid, fileLock->userp,
!                                 &req, &connp);
!                 if (code) 
!                     break;
! 
!                 callp = cm_GetRxConn(connp);
!                 code = RXAFS_ExtendLock(callp, &tfid,
!                                          &volSync);
!                 rx_PutConnection(callp);
! 
!             } while (cm_Analyze(connp, fileLock->userp, &req,
!                                  &fileLock->fid, &volSync, NULL, NULL,
!                                  code));
!             code = cm_MapRPCError(code, &req);
!             lock_ObtainWrite(&cm_scacheLock);
!         }
!         q = nq;
!     }
!     lock_ReleaseWrite(&cm_scacheLock);
! }       
  
  long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
  {
!     long code;
!     int Which = ((oldFileLock->LockType & 0x1) ? LockRead : LockWrite);
!     cm_scache_t *scp;
!     AFSFid tfid;
!     AFSVolSync volSync;
!     cm_conn_t *connp;
!     cm_file_lock_t *fileLock;
!     osi_queue_t *q;
!     cm_req_t req;
!     int found = 0;
!     struct rx_connection * callp;
! 
!     if (vcp_is_dead) {
!         code = CM_ERROR_TIMEDOUT;
!         goto handleCode;
!     }
! 
!     cm_InitReq(&req);
! 
!     /* Look for a conflict.  Also, if we are asking for a shared lock,
!      * look for another shared lock, so we don't have to do an RPC.
!      */
!     code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
!     if (code)
!         return code;
! 
!     q = scp->fileLocks;
!     while (q) {
!         fileLock = (cm_file_lock_t *)
!             ((char *) q - offsetof(cm_file_lock_t, fileq));
!         if ((fileLock->flags &
!               (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
!              == 0) {
!             if ((oldFileLock->LockType & 0x1) == 0
!                  || (fileLock->LockType & 0x1) == 0) {
!                 cm_ReleaseSCache(scp);
!                 return CM_ERROR_WOULDBLOCK;
!             }
!             found = 1;
!         }
!         q = osi_QNext(q);
!     }
! 
!     if (found)
!         code = 0;
!     else {
!         tfid.Volume = oldFileLock->fid.volume;
!         tfid.Vnode = oldFileLock->fid.vnode;
!         tfid.Unique = oldFileLock->fid.unique;
!         do {
!             code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
!                             &req, &connp);
!             if (code) 
!                 break;
! 
!             callp = cm_GetRxConn(connp);
!             code = RXAFS_SetLock(callp, &tfid, Which,
!                                   &volSync);
!             rx_PutConnection(callp);
! 
!         } while (cm_Analyze(connp, oldFileLock->userp, &req,
!                              &oldFileLock->fid, &volSync,
!                              NULL, NULL, code));
!         code = cm_MapRPCError(code, &req);
!     }
  
    handleCode:
!     if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
!         lock_ObtainMutex(&scp->mx);
!         osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
!         lock_ReleaseMutex(&scp->mx);
!     }
!     lock_ObtainWrite(&cm_scacheLock);
!     if (code == 0)
!         oldFileLock->flags = 0;
!     else if (code != CM_ERROR_WOULDBLOCK) {
!         oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
!         cm_ReleaseUser(oldFileLock->userp);
          oldFileLock->userp = NULL;
!     }
!     lock_ReleaseWrite(&cm_scacheLock);
  
!     return code;
  }
Index: openafs/src/WINNT/afsd/cm_vnodeops.h
diff -c openafs/src/WINNT/afsd/cm_vnodeops.h:1.5 openafs/src/WINNT/afsd/cm_vnodeops.h:1.5.2.3
*** openafs/src/WINNT/afsd/cm_vnodeops.h:1.5	Mon Jun 21 13:25:35 2004
--- openafs/src/WINNT/afsd/cm_vnodeops.h	Mon Oct 18 00:09:26 2004
***************
*** 15,21 ****
  /* parms for attribute setting call */
  typedef struct cm_attr {
  	int mask;
! 	unsigned long clientModTime;
          osi_hyper_t length;
  	int unixModeBits;
          long owner;
--- 15,21 ----
  /* parms for attribute setting call */
  typedef struct cm_attr {
  	int mask;
! 	time_t clientModTime;
          osi_hyper_t length;
  	int unixModeBits;
          long owner;
***************
*** 70,75 ****
--- 70,79 ----
  extern long cm_Lookup(cm_scache_t *dscp, char *namep, long flags,
  	cm_user_t *userp, cm_req_t *reqp, cm_scache_t **outpScpp);
  
+ extern long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags,
+                               cm_user_t *userp, cm_req_t *reqp, 
+                               cm_scache_t **outpScpp);
+ 
  extern void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp,
  	cm_user_t *userp, cm_req_t *reqp);
  
***************
*** 104,113 ****
  extern long cm_HandleLink(cm_scache_t *linkScp, struct cm_user *userp,
  	cm_req_t *reqp);
  
  extern long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp,
  	long flags, cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp);
  
! extern int cm_ExpandSysName(char *inp, char *outp, long outSize);
  
  extern long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp);
  
--- 108,121 ----
  extern long cm_HandleLink(cm_scache_t *linkScp, struct cm_user *userp,
  	cm_req_t *reqp);
  
+ extern long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp,
+     long flags, cm_user_t *userp, cm_req_t *reqp);
+ 
  extern long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp,
  	long flags, cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp);
  
! extern int cm_ExpandSysName(char *inp, char *outp, long outSize,
!                             unsigned int sysNameIndex);
  
  extern long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp);
  
Index: openafs/src/WINNT/afsd/cm_volume.h
diff -c openafs/src/WINNT/afsd/cm_volume.h:1.3 openafs/src/WINNT/afsd/cm_volume.h:1.3.2.1
*** openafs/src/WINNT/afsd/cm_volume.h:1.3	Wed Aug  4 11:52:56 2004
--- openafs/src/WINNT/afsd/cm_volume.h	Mon Oct 18 00:09:27 2004
***************
*** 20,26 ****
  	struct cm_fid *dotdotFidp;	/* parent of volume root */
      osi_mutex_t mx;
      long flags;			/* by mx */
!     int refCount;			/* by cm_volumeLock */
      cm_serverRef_t *rwServersp;	/* by mx */
      cm_serverRef_t *roServersp;	/* by mx */
      cm_serverRef_t *bkServersp;	/* by mx */
--- 20,26 ----
  	struct cm_fid *dotdotFidp;	/* parent of volume root */
      osi_mutex_t mx;
      long flags;			/* by mx */
!     unsigned long refCount;			/* by cm_volumeLock */
      cm_serverRef_t *rwServersp;	/* by mx */
      cm_serverRef_t *roServersp;	/* by mx */
      cm_serverRef_t *bkServersp;	/* by mx */
Index: openafs/src/WINNT/afsd/ctokens.c
diff -c openafs/src/WINNT/afsd/ctokens.c:1.2 openafs/src/WINNT/afsd/ctokens.c:1.2.20.1
*** openafs/src/WINNT/afsd/ctokens.c:1.2	Sat Nov  4 05:01:41 2000
--- openafs/src/WINNT/afsd/ctokens.c	Mon Oct 18 00:09:27 2004
***************
*** 88,94 ****
  			if (tokenExpireTime <= current_time)
  				printf("[>> Expired <<]\n");
  			else {
! 				expireString = ctime(&tokenExpireTime);
  				expireString += 4;	 /* Skip day of week */
  				expireString[12] = '\0'; /* Omit secs & year */
  				printf("[Expires %s]\n", expireString);
--- 88,95 ----
  			if (tokenExpireTime <= current_time)
  				printf("[>> Expired <<]\n");
  			else {
!                                 time_t t = tokenExpireTime;
! 				expireString = ctime(&t);
  				expireString += 4;	 /* Skip day of week */
  				expireString[12] = '\0'; /* Omit secs & year */
  				printf("[Expires %s]\n", expireString);
Index: openafs/src/WINNT/afsd/fs.c
diff -c openafs/src/WINNT/afsd/fs.c:1.16.2.1 openafs/src/WINNT/afsd/fs.c:1.16.2.3
*** openafs/src/WINNT/afsd/fs.c:1.16.2.1	Mon Aug 23 11:55:02 2004
--- openafs/src/WINNT/afsd/fs.c	Mon Oct 18 00:09:27 2004
***************
*** 609,616 ****
              return FALSE;
          }
  
-         fTested = TRUE;
- 
          dwSize = 0;
          dwSize2 = 0;
  
--- 609,614 ----
***************
*** 645,683 ****
  
              if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken))
              {
!                 /* We'll have to allocate a chunk of memory to store the list of
!                  * groups to which this user belongs; find out how much memory
!                  * we'll need.
!                  */
!                 DWORD dwSize = 0;
!                 PTOKEN_GROUPS pGroups;
!                 
!                 GetTokenInformation (hToken, TokenGroups, NULL, dwSize, &dwSize);
!             
!                 pGroups = (PTOKEN_GROUPS)malloc(dwSize);
!                 
!                 /* Allocate that buffer, and read in the list of groups. */
!                 if (GetTokenInformation (hToken, TokenGroups, pGroups, dwSize, &dwSize))
!                 {
!                     /* Look through the list of group SIDs and see if any of them
!                      * matches the AFS Client Admin group SID.
                       */
!                     size_t iGroup = 0;
!                     for (; (!fAdmin) && (iGroup < pGroups->GroupCount); ++iGroup)
                      {
!                         if (EqualSid (psidAdmin, pGroups->Groups[ iGroup ].Sid)) {
!                             fAdmin = TRUE;
                          }
                      }
                  }
  
!                 if (pGroups)
!                     free(pGroups);
              }
          }
  
          free(psidAdmin);
          free(pszRefDomain);
      }
  
      return fAdmin;
--- 643,718 ----
  
              if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken))
              {
! 
!                 if (!CheckTokenMembership(hToken, psidAdmin, &fAdmin)) {
!                     /* We'll have to allocate a chunk of memory to store the list of
!                      * groups to which this user belongs; find out how much memory
!                      * we'll need.
                       */
!                     DWORD dwSize = 0;
!                     PTOKEN_GROUPS pGroups;
! 
!                     GetTokenInformation (hToken, TokenGroups, NULL, dwSize, &dwSize);
! 
!                     pGroups = (PTOKEN_GROUPS)malloc(dwSize);
! 
!                     /* Allocate that buffer, and read in the list of groups. */
!                     if (GetTokenInformation (hToken, TokenGroups, pGroups, dwSize, &dwSize))
                      {
!                         /* Look through the list of group SIDs and see if any of them
!                          * matches the AFS Client Admin group SID.
!                          */
!                         size_t iGroup = 0;
!                         for (; (!fAdmin) && (iGroup < pGroups->GroupCount); ++iGroup)
!                         {
!                             if (EqualSid (psidAdmin, pGroups->Groups[ iGroup ].Sid)) {
!                                 fAdmin = TRUE;
!                             }
                          }
                      }
+ 
+                     if (pGroups)
+                         free(pGroups);
                  }
  
!                 /* if do not have permission because we were not explicitly listed
!                  * in the Admin Client Group let's see if we are the SYSTEM account
!                  */
!                 if (!fAdmin) {
!                     PTOKEN_USER pTokenUser;
!                     SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
!                     PSID pSidLocalSystem = 0;
!                     DWORD gle;
! 
!                     GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize);
! 
!                     pTokenUser = (PTOKEN_USER)malloc(dwSize);
! 
!                     if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwSize, &dwSize))
!                         gle = GetLastError();
! 
!                     if (AllocateAndInitializeSid( &SIDAuth, 1,
!                                                   SECURITY_LOCAL_SYSTEM_RID,
!                                                   0, 0, 0, 0, 0, 0, 0,
!                                                   &pSidLocalSystem))
!                     {
!                         if (EqualSid(pTokenUser->User.Sid, pSidLocalSystem)) {
!                             fAdmin = TRUE;
!                         }
! 
!                         FreeSid(pSidLocalSystem);
!                     }
! 
!                     if ( pTokenUser )
!                         free(pTokenUser);
!                 }
              }
          }
  
          free(psidAdmin);
          free(pszRefDomain);
+ 
+         fTested = TRUE;
      }
  
      return fAdmin;
***************
*** 2181,2199 ****
          return 0;
      }
  
! 	input = space;
! 	memcpy(&setp, input, sizeof(afs_int32));
! 	input += sizeof(afs_int32);
! 	if (!setp) {
! 	    fprintf(stderr,"No sysname name value was found\n");
          return 1;
! 	} 
      
      printf("Current sysname%s", setp > 1 ? "s are" : " is");
      for (; setp > 0; --setp ) {
          printf(" \'%s\'", input);
          input += strlen(input) + 1;
! 	}
      printf("\n");
      return 0;
  }
--- 2216,2234 ----
          return 0;
      }
  
!     input = space;
!     memcpy(&setp, input, sizeof(afs_int32));
!     input += sizeof(afs_int32);
!     if (!setp) {
!         fprintf(stderr,"No sysname name value was found\n");
          return 1;
!     } 
      
      printf("Current sysname%s", setp > 1 ? "s are" : " is");
      for (; setp > 0; --setp ) {
          printf(" \'%s\'", input);
          input += strlen(input) + 1;
!     }
      printf("\n");
      return 0;
  }
***************
*** 2447,2453 ****
  	}
  	else {
  	    /* got a ticket */
! 	    if (ttoken.kvno >= 0 && ttoken.kvno	<= 255)	scIndex	= 2;	/* kerberos */
  	    else {
  		fprintf (stderr, "fs: funny kvno (%d) in ticket, proceeding\n",
  			 ttoken.kvno);
--- 2482,2488 ----
  	}
  	else {
  	    /* got a ticket */
! 	    if (ttoken.kvno >= 0 && ttoken.kvno	<= 256)	scIndex	= 2;	/* kerberos */
  	    else {
  		fprintf (stderr, "fs: funny kvno (%d) in ticket, proceeding\n",
  			 ttoken.kvno);
***************
*** 2943,2949 ****
  char **argv; {
      register afs_int32 code;
      register struct cmd_syndesc *ts;
!     
  #ifdef	AFS_AIX32_ENV
      /*
       * The following signal action for AIX is necessary so that in case of a 
--- 2978,2984 ----
  char **argv; {
      register afs_int32 code;
      register struct cmd_syndesc *ts;
! 
  #ifdef	AFS_AIX32_ENV
      /*
       * The following signal action for AIX is necessary so that in case of a 
***************
*** 3141,3147 ****
      cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
  
      ts = cmd_CreateSyntax("sysname", SysNameCmd, 0, "get/set sysname (i.e. @sys) value");
!     cmd_AddParm(ts, "-newsys", CMD_SINGLE, CMD_OPTIONAL, "new sysname");
  
      ts = cmd_CreateSyntax("exportafs", ExportAfsCmd, 0, "enable/disable translators to AFS");
      cmd_AddParm(ts, "-type", CMD_SINGLE, 0, "exporter name");
--- 3176,3182 ----
      cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
  
      ts = cmd_CreateSyntax("sysname", SysNameCmd, 0, "get/set sysname (i.e. @sys) value");
!     cmd_AddParm(ts, "-newsys", CMD_LIST, CMD_OPTIONAL, "new sysname");
  
      ts = cmd_CreateSyntax("exportafs", ExportAfsCmd, 0, "enable/disable translators to AFS");
      cmd_AddParm(ts, "-type", CMD_SINGLE, 0, "exporter name");
Index: openafs/src/WINNT/afsd/lanahelper.cpp
diff -c openafs/src/WINNT/afsd/lanahelper.cpp:1.8 openafs/src/WINNT/afsd/lanahelper.cpp:1.8.2.1
*** openafs/src/WINNT/afsd/lanahelper.cpp:1.8	Wed Jul 14 02:00:18 2004
--- openafs/src/WINNT/afsd/lanahelper.cpp	Sun Oct  3 09:35:15 2004
***************
*** 394,399 ****
--- 394,428 ----
      return LANA_INVALID;
  }
  
+ /* Returns TRUE if all adapters are loopback adapters */
+ extern "C" BOOL lana_OnlyLoopback(void)
+ {
+     NCB ncb;
+     LANA_ENUM lana_list;
+     int status;
+     int i;
+ 
+     memset(&ncb, 0, sizeof(ncb));
+     ncb.ncb_command = NCBENUM;
+     ncb.ncb_buffer = (UCHAR *) &lana_list;
+     ncb.ncb_length = sizeof(lana_list);
+     status = Netbios(&ncb);
+     if (status != 0) {
+ #ifndef NOLOGGING
+         afsi_log("Netbios NCBENUM failed: status %ld", status);
+ #endif
+         return FALSE;
+     }
+     for (i = 0; i < lana_list.length; i++) {
+ 	if (!lana_IsLoopback(lana_list.lana[i])) {
+ 	    // Found one non-Loopback adapter
+ 	    return FALSE;
+ 	}
+     }
+     // All adapters are loopback
+     return TRUE;
+ }
+ 
  // Is the given lana a Windows Loopback Adapter?
  // TODO: implement a better check for loopback
  // TODO: also check for proper bindings (IPv4)
Index: openafs/src/WINNT/afsd/lanahelper.h
diff -c openafs/src/WINNT/afsd/lanahelper.h:1.3 openafs/src/WINNT/afsd/lanahelper.h:1.3.2.1
*** openafs/src/WINNT/afsd/lanahelper.h:1.3	Wed Jul 14 02:00:18 2004
--- openafs/src/WINNT/afsd/lanahelper.h	Sun Oct  3 09:35:15 2004
***************
*** 59,64 ****
--- 59,66 ----
  
    lana_number_t lana_FindLoopback(void);
  
+   BOOL lana_OnlyLoopback(void);
+ 
    BOOL lana_IsLoopback(lana_number_t lana);
  
    long lana_GetUncServerNameEx(char *buffer, lana_number_t * pLana, int * pIsGateway, int flags);
Index: openafs/src/WINNT/afsd/smb.c
diff -c openafs/src/WINNT/afsd/smb.c:1.55.2.1 openafs/src/WINNT/afsd/smb.c:1.55.2.3
*** openafs/src/WINNT/afsd/smb.c:1.55.2.1	Wed Aug 18 13:11:22 2004
--- openafs/src/WINNT/afsd/smb.c	Mon Oct 18 00:09:27 2004
***************
*** 7,13 ****
   * directory or online at http://www.openafs.org/dl/license10.html
   */
  
! //#define NOSERVICE 1
  
  #include <afs/param.h>
  #include <afs/stds.h>
--- 7,14 ----
   * directory or online at http://www.openafs.org/dl/license10.html
   */
  
! //#define NOTSERVICE 1
! #define LOG_PACKET 1
  
  #include <afs/param.h>
  #include <afs/stds.h>
***************
*** 189,194 ****
--- 190,198 ----
         *(sizep) = strlen(cm_HostName)
  #endif /* DJGPP */
  
+ #ifdef LOG_PACKET
+ void smb_LogPacket(smb_packet_t *packet);
+ #endif /* LOG_PACKET */
  extern char AFSConfigKeyName[];
  
  char smb_ServerDomainName[MAX_COMPUTERNAME_LENGTH + 1] = ""; /* domain name */
***************
*** 201,556 ****
  /* Faux server GUID. This is never checked. */
  GUID smb_ServerGUID = { 0x40015cb8, 0x058a, 0x44fc, { 0xae, 0x7e, 0xbb, 0x29, 0x52, 0xee, 0x7e, 0xff }};
  
- /*
-  * Demo expiration
-  *
-  * To build an expiring version, comment out the definition of NOEXPIRE,
-  * and set the definition of EXPIREDATE to the desired value.
-  */
- #define NOEXPIRE 1
- #define EXPIREDATE 834000000		/* Wed Jun 5 1996 */
- 
- 
  char * myCrt_Dispatch(int i)
  {
! 	switch (i)
! 	{
! 	default:
! 		return "unknown SMB op";
! 	case 0x00:
! 		return "(00)ReceiveCoreMakeDir";
! 	case 0x01:
! 		return "(01)ReceiveCoreRemoveDir";
! 	case 0x02:
! 		return "(02)ReceiveCoreOpen";
! 	case 0x03:
! 		return "(03)ReceiveCoreCreate";
! 	case 0x04:
! 		return "(04)ReceiveCoreClose";
! 	case 0x05:
! 		return "(05)ReceiveCoreFlush";
! 	case 0x06:
! 		return "(06)ReceiveCoreUnlink";
! 	case 0x07:
! 		return "(07)ReceiveCoreRename";
! 	case 0x08:
! 		return "(08)ReceiveCoreGetFileAttributes";
! 	case 0x09:
! 		return "(09)ReceiveCoreSetFileAttributes";
! 	case 0x0a:
! 		return "(0a)ReceiveCoreRead";
! 	case 0x0b:
! 		return "(0b)ReceiveCoreWrite";
! 	case 0x0c:
! 		return "(0c)ReceiveCoreLockRecord";
! 	case 0x0d:
! 		return "(0d)ReceiveCoreUnlockRecord";
! 	case 0x0e:
! 		return "(0e)SendCoreBadOp";
! 	case 0x0f:
! 		return "(0f)ReceiveCoreCreate";
! 	case 0x10:
! 		return "(10)ReceiveCoreCheckPath";
! 	case 0x11:
! 		return "(11)SendCoreBadOp";
! 	case 0x12:
! 		return "(12)ReceiveCoreSeek";
! 	case 0x1a:
! 		return "(1a)ReceiveCoreReadRaw";
! 	case 0x1d:
! 		return "(1d)ReceiveCoreWriteRawDummy";
! 	case 0x22:
! 		return "(22)ReceiveV3SetAttributes";
! 	case 0x23:
! 		return "(23)ReceiveV3GetAttributes";
! 	case 0x24:
! 		return "(24)ReceiveV3LockingX";
! 	case 0x25:
! 		return "(25)ReceiveV3Trans";
! 	case 0x26:
! 		return "(26)ReceiveV3Trans[aux]";
! 	case 0x29:
! 		return "(29)SendCoreBadOp";
! 	case 0x2b:
! 		return "(2b)ReceiveCoreEcho";
! 	case 0x2d:
! 		return "(2d)ReceiveV3OpenX";
! 	case 0x2e:
! 		return "(2e)ReceiveV3ReadX";
! 	case 0x32:
! 		return "(32)ReceiveV3Tran2A";
! 	case 0x33:
! 		return "(33)ReceiveV3Tran2A[aux]";
! 	case 0x34:
! 		return "(34)ReceiveV3FindClose";
! 	case 0x35:
! 		return "(35)ReceiveV3FindNotifyClose";
! 	case 0x70:
! 		return "(70)ReceiveCoreTreeConnect";
! 	case 0x71:
! 		return "(71)ReceiveCoreTreeDisconnect";
! 	case 0x72:
! 		return "(72)ReceiveNegotiate";
! 	case 0x73:
! 		return "(73)ReceiveV3SessionSetupX";
! 	case 0x74:
! 		return "(74)ReceiveV3UserLogoffX";
! 	case 0x75:
! 		return "(75)ReceiveV3TreeConnectX";
! 	case 0x80:
! 		return "(80)ReceiveCoreGetDiskAttributes";
! 	case 0x81:
! 		return "(81)ReceiveCoreSearchDir";
! 	case 0xA0:
! 		return "(A0)ReceiveNTTransact";
! 	case 0xA2:
! 		return "(A2)ReceiveNTCreateX";
! 	case 0xA4:
! 		return "(A4)ReceiveNTCancel";
! 	case 0xc0:
! 		return "(c0)SendCoreBadOp";
! 	case 0xc1:
! 		return "(c1)SendCoreBadOp";
! 	case 0xc2:
! 		return "(c2)SendCoreBadOp";
! 	case 0xc3:
! 		return "(c3)SendCoreBadOp";
! 	}
! }
  
  char * myCrt_2Dispatch(int i)
  {
! 	switch (i)
! 	{
! 	default:
! 		return "unknown SMB op-2";
! 	case 0:
! 		return "S(00)CreateFile";
! 	case 1:
! 		return "S(01)FindFirst";
! 	case 2:
! 		return "S(02)FindNext";	/* FindNext */
! 	case 3:
! 		return "S(03)QueryFileSystem_ReceiveTran2QFSInfo";
! 	case 4:
! 		return "S(04)??";
! 	case 5:
! 		return "S(05)QueryFileInfo_ReceiveTran2QPathInfo";
! 	case 6:
! 		return "S(06)SetFileInfo_ReceiveTran2SetPathInfo";
! 	case 7:
! 		return "S(07)SetInfoHandle_ReceiveTran2QFileInfo";
! 	case 8:
! 		return "S(08)??_ReceiveTran2SetFileInfo";
! 	case 9:
! 		return "S(09)??_ReceiveTran2FSCTL";
! 	case 10:
! 		return "S(0a)_ReceiveTran2IOCTL";
! 	case 11:
! 		return "S(0b)_ReceiveTran2FindNotifyFirst";
! 	case 12:
! 		return "S(0c)_ReceiveTran2FindNotifyNext";
! 	case 13:
! 		return "S(0d)CreateDirectory_ReceiveTran2MKDir";
! 	}
! }
  
  char * myCrt_RapDispatch(int i)
  {
! 	switch(i)
! 	{
! 	default:
! 		return "unknown RAP OP";
! 	case 0:
! 		return "RAP(0)NetShareEnum";
! 	case 1:
! 		return "RAP(1)NetShareGetInfo";
! 	case 13:
! 		return "RAP(13)NetServerGetInfo";
! 	case 63:
! 		return "RAP(63)NetWkStaGetInfo";
! 	}
! }
  
  /* scache must be locked */
  unsigned int smb_Attributes(cm_scache_t *scp)
  {
! 	unsigned int attrs;
  
! 	if (scp->fileType == CM_SCACHETYPE_DIRECTORY
! 		|| scp->fileType == CM_SCACHETYPE_MOUNTPOINT)
! 		attrs = SMB_ATTR_DIRECTORY;
! 	else
! 		attrs = 0;
! 
! 	/*
! 	 * We used to mark a file RO if it was in an RO volume, but that
! 	 * turns out to be impolitic in NT.  See defect 10007.
! 	 */
  #ifdef notdef
! 	if ((scp->unixModeBits & 0222) == 0 || (scp->flags & CM_SCACHEFLAG_RO))
  #endif
  	if ((scp->unixModeBits & 0222) == 0)
! 		attrs |= SMB_ATTR_READONLY;	/* turn on read-only flag */
  
! 	return attrs;
  }
  
  /* Check if the named file/dir is a dotfile/dotdir */
  /* String pointed to by lastComp can have leading slashes, but otherwise should have
     no other patch components */
  unsigned int smb_IsDotFile(char *lastComp) {
! 	char *s;
! 	if(lastComp) {
! 		/* skip over slashes */
          for(s=lastComp;*s && (*s == '\\' || *s == '/'); s++);
! 	}
! 	else
! 		return 0;
  
      /* nulls, curdir and parent dir doesn't count */
! 	if(!*s) return 0;
! 	if(*s == '.') {
! 		if(!*(s + 1)) return 0;
! 		if(*(s+1) == '.' && !*(s + 2)) return 0;
! 		return 1;
! 	}
! 	return 0;
  }
  
  static int ExtractBits(WORD bits, short start, short len)
  {
! 	int end;
! 	WORD num;
  
! 	end = start + len;
          
! 	num = bits << (16 - end);
! 	num = num >> ((16 - end) + start);
  
! 	return (int)num;
  }
  
  #ifndef DJGPP
  void ShowUnixTime(char *FuncName, time_t unixTime)
  {
! 	FILETIME ft;
! 	WORD wDate, wTime;
  
! 	smb_LargeSearchTimeFromUnixTime(&ft, unixTime);
!                 
! 	if (!FileTimeToDosDateTime(&ft, &wDate, &wTime))
! 		osi_Log1(smb_logp, "Failed to convert filetime to dos datetime: %d", GetLastError());
! 	else {
! 		int day, month, year, sec, min, hour;
! 		char msg[256];
! 
! 		day = ExtractBits(wDate, 0, 5);
! 		month = ExtractBits(wDate, 5, 4);
! 		year = ExtractBits(wDate, 9, 7) + 1980;
! 
! 		sec = ExtractBits(wTime, 0, 5);
! 		min = ExtractBits(wTime, 5, 6);
! 		hour = ExtractBits(wTime, 11, 5);
! 
! 		sprintf(msg, "%s = %02d-%02d-%04d %02d:%02d:%02d", FuncName, month, day, year, hour, min, sec);
! 		osi_Log1(smb_logp, "%s", osi_LogSaveString(smb_logp, msg));
! 	}
! }
  #endif /* DJGPP */
  
  #ifndef DJGPP
  /* Determine if we are observing daylight savings time */
  void GetTimeZoneInfo(BOOL *pDST, LONG *pDstBias, LONG *pBias)
  {
! 	TIME_ZONE_INFORMATION timeZoneInformation;
! 	SYSTEMTIME utc, local, localDST;
  
! 	/* Get the time zone info. NT uses this to calc if we are in DST. */
! 	GetTimeZoneInformation(&timeZoneInformation);
!  
! 	/* Return the daylight bias */
! 	*pDstBias = timeZoneInformation.DaylightBias;
  
! 	/* Return the bias */
! 	*pBias = timeZoneInformation.Bias;
  
! 	/* Now determine if DST is being observed */
  
! 	/* Get the UTC (GMT) time */
! 	GetSystemTime(&utc);
! 
! 	/* Convert UTC time to local time using the time zone info.  If we are
! 	   observing DST, the calculated local time will include this. 
! 	*/
! 	SystemTimeToTzSpecificLocalTime(&timeZoneInformation, &utc, &localDST);
! 
! 	/* Set the daylight bias to 0.  The daylight bias is the amount of change
! 	   in time that we use for daylight savings time.  By setting this to 0
! 	   we cause there to be no change in time during daylight savings time. 
! 	*/
! 	timeZoneInformation.DaylightBias = 0;
! 
! 	/* Convert the utc time to local time again, but this time without any
! 	   adjustment for daylight savings time. 
! 	*/
! 	SystemTimeToTzSpecificLocalTime(&timeZoneInformation, &utc, &local);
! 
! 	/* If the two times are different, then it means that the localDST that
! 	   we calculated includes the daylight bias, and therefore we are
! 	   observing daylight savings time.
! 	*/
! 	*pDST = localDST.wHour != local.wHour;
! }
  #else
  /* Determine if we are observing daylight savings time */
  void GetTimeZoneInfo(BOOL *pDST, LONG *pDstBias, LONG *pBias)
  {
! 	struct timeb t;
  
! 	ftime(&t);
! 	*pDST = t.dstflag;
! 	*pDstBias = -60;    /* where can this be different? */
! 	*pBias = t.timezone;
! }
  #endif /* DJGPP */
   
  
  void CompensateForSmbClientLastWriteTimeBugs(long *pLastWriteTime)
  {
! 	BOOL dst;       /* Will be TRUE if observing DST */
! 	LONG dstBias;   /* Offset from local time if observing DST */
! 	LONG bias;      /* Offset from GMT for local time */
! 
! 	/*
! 	 * This function will adjust the last write time to compensate
! 	 * for two bugs in the smb client:
! 	 *
! 	 *    1) During Daylight Savings Time, the LastWriteTime is ahead
! 	 *       in time by the DaylightBias (ignoring the sign - the
! 	 *       DaylightBias is always stored as a negative number).  If
! 	 *       the DaylightBias is -60, then the LastWriteTime will be
! 	 *       ahead by 60 minutes.
! 	 *
! 	 *    2) If the local time zone is a positive offset from GMT, then
! 	 *       the LastWriteTime will be the correct local time plus the
! 	 *       Bias (ignoring the sign - a positive offset from GMT is
! 	 *       always stored as a negative Bias).  If the Bias is -120,
! 	 *       then the LastWriteTime will be ahead by 120 minutes.
! 	 *
! 	 *    These bugs can occur at the same time.
! 	 */
! 
! 	GetTimeZoneInfo(&dst, &dstBias, &bias);
! 
! 	/* First adjust for DST */
! 	if (dst)
! 		*pLastWriteTime -= (-dstBias * 60);     /* Convert dstBias to seconds */
! 
! 	/* Now adjust for a positive offset from GMT (a negative bias). */
! 	if (bias < 0)
! 		*pLastWriteTime -= (-bias * 60);        /* Convert bias to seconds */
! }		
  
  /*
   * Calculate the difference (in seconds) between local time and GMT.
--- 205,569 ----
  /* Faux server GUID. This is never checked. */
  GUID smb_ServerGUID = { 0x40015cb8, 0x058a, 0x44fc, { 0xae, 0x7e, 0xbb, 0x29, 0x52, 0xee, 0x7e, 0xff }};
  
  char * myCrt_Dispatch(int i)
  {
!     switch (i)
!     {
!     case 0x00:
!         return "(00)ReceiveCoreMakeDir";
!     case 0x01:
!         return "(01)ReceiveCoreRemoveDir";
!     case 0x02:
!         return "(02)ReceiveCoreOpen";
!     case 0x03:
!         return "(03)ReceiveCoreCreate";
!     case 0x04:
!         return "(04)ReceiveCoreClose";
!     case 0x05:
!         return "(05)ReceiveCoreFlush";
!     case 0x06:
!         return "(06)ReceiveCoreUnlink";
!     case 0x07:
!         return "(07)ReceiveCoreRename";
!     case 0x08:
!         return "(08)ReceiveCoreGetFileAttributes";
!     case 0x09:
!         return "(09)ReceiveCoreSetFileAttributes";
!     case 0x0a:
!         return "(0a)ReceiveCoreRead";
!     case 0x0b:
!         return "(0b)ReceiveCoreWrite";
!     case 0x0c:
!         return "(0c)ReceiveCoreLockRecord";
!     case 0x0d:
!         return "(0d)ReceiveCoreUnlockRecord";
!     case 0x0e:
!         return "(0e)SendCoreBadOp";
!     case 0x0f:
!         return "(0f)ReceiveCoreCreate";
!     case 0x10:
!         return "(10)ReceiveCoreCheckPath";
!     case 0x11:
!         return "(11)SendCoreBadOp";
!     case 0x12:
!         return "(12)ReceiveCoreSeek";
!     case 0x1a:
!         return "(1a)ReceiveCoreReadRaw";
!     case 0x1d:
!         return "(1d)ReceiveCoreWriteRawDummy";
!     case 0x22:
!         return "(22)ReceiveV3SetAttributes";
!     case 0x23:
!         return "(23)ReceiveV3GetAttributes";
!     case 0x24:
!         return "(24)ReceiveV3LockingX";
!     case 0x25:
!         return "(25)ReceiveV3Trans";
!     case 0x26:
!         return "(26)ReceiveV3Trans[aux]";
!     case 0x29:
!         return "(29)SendCoreBadOp";
!     case 0x2b:
!         return "(2b)ReceiveCoreEcho";
!     case 0x2d:
!         return "(2d)ReceiveV3OpenX";
!     case 0x2e:
!         return "(2e)ReceiveV3ReadX";
!     case 0x32:
!         return "(32)ReceiveV3Tran2A";
!     case 0x33:
!         return "(33)ReceiveV3Tran2A[aux]";
!     case 0x34:
!         return "(34)ReceiveV3FindClose";
!     case 0x35:
!         return "(35)ReceiveV3FindNotifyClose";
!     case 0x70:
!         return "(70)ReceiveCoreTreeConnect";
!     case 0x71:
!         return "(71)ReceiveCoreTreeDisconnect";
!     case 0x72:
!         return "(72)ReceiveNegotiate";
!     case 0x73:
!         return "(73)ReceiveV3SessionSetupX";
!     case 0x74:
!         return "(74)ReceiveV3UserLogoffX";
!     case 0x75:
!         return "(75)ReceiveV3TreeConnectX";
!     case 0x80:
!         return "(80)ReceiveCoreGetDiskAttributes";
!     case 0x81:
!         return "(81)ReceiveCoreSearchDir";
!     case 0x82:
!         return "(82)Find";
!     case 0x83:
!         return "(83)FindUnique";
!     case 0x84:
!         return "(84)FindClose";
!     case 0xA0:
!         return "(A0)ReceiveNTTransact";
!     case 0xA2:
!         return "(A2)ReceiveNTCreateX";
!     case 0xA4:
!         return "(A4)ReceiveNTCancel";
!     case 0xA5:
!         return "(A5)ReceiveNTRename";
!     case 0xc0:
!         return "(C0)OpenPrintFile";
!     case 0xc1:
!         return "(C1)WritePrintFile";
!     case 0xc2:
!         return "(C2)ClosePrintFile";
!     case 0xc3:
!         return "(C3)GetPrintQueue";
!     case 0xd8:
!         return "(D8)ReadBulk";
!     case 0xd9:
!         return "(D9)WriteBulk";
!     case 0xda:
!         return "(DA)WriteBulkData";
!     default:
!         return "unknown SMB op";
!     }
! }       
  
  char * myCrt_2Dispatch(int i)
  {
!     switch (i)
!     {
!     default:
!         return "unknown SMB op-2";
!     case 0:
!         return "S(00)CreateFile";
!     case 1:
!         return "S(01)FindFirst";
!     case 2:
!         return "S(02)FindNext";	/* FindNext */
!     case 3:
!         return "S(03)QueryFileSystem_ReceiveTran2QFSInfo";
!     case 4:
!         return "S(04)??";
!     case 5:
!         return "S(05)QueryFileInfo_ReceiveTran2QPathInfo";
!     case 6:
!         return "S(06)SetFileInfo_ReceiveTran2SetPathInfo";
!     case 7:
!         return "S(07)SetInfoHandle_ReceiveTran2QFileInfo";
!     case 8:
!         return "S(08)??_ReceiveTran2SetFileInfo";
!     case 9:
!         return "S(09)??_ReceiveTran2FSCTL";
!     case 10:
!         return "S(0a)_ReceiveTran2IOCTL";
!     case 11:
!         return "S(0b)_ReceiveTran2FindNotifyFirst";
!     case 12:
!         return "S(0c)_ReceiveTran2FindNotifyNext";
!     case 13:
!         return "S(0d)_ReceiveTran2CreateDirectory";
!     case 14:
!         return "S(0e)_ReceiveTran2SessionSetup";
!     }
! }       
  
  char * myCrt_RapDispatch(int i)
  {
!     switch(i)
!     {
!     default:
!         return "unknown RAP OP";
!     case 0:
!         return "RAP(0)NetShareEnum";
!     case 1:
!         return "RAP(1)NetShareGetInfo";
!     case 13:
!         return "RAP(13)NetServerGetInfo";
!     case 63:
!         return "RAP(63)NetWkStaGetInfo";
!     }
! }       
  
  /* scache must be locked */
  unsigned int smb_Attributes(cm_scache_t *scp)
  {
!     unsigned int attrs;
! 
!     if (scp->fileType == CM_SCACHETYPE_DIRECTORY
!          || scp->fileType == CM_SCACHETYPE_MOUNTPOINT)
!         attrs = SMB_ATTR_DIRECTORY;
!     else
!         attrs = 0;
  
!     /*
!      * We used to mark a file RO if it was in an RO volume, but that
!      * turns out to be impolitic in NT.  See defect 10007.
!      */
  #ifdef notdef
!     if ((scp->unixModeBits & 0222) == 0 || (scp->flags & CM_SCACHEFLAG_RO))
  #endif
  	if ((scp->unixModeBits & 0222) == 0)
!             attrs |= SMB_ATTR_READONLY;	/* turn on read-only flag */
  
!     return attrs;
  }
  
  /* Check if the named file/dir is a dotfile/dotdir */
  /* String pointed to by lastComp can have leading slashes, but otherwise should have
     no other patch components */
  unsigned int smb_IsDotFile(char *lastComp) {
!     char *s;
!     if(lastComp) {
!         /* skip over slashes */
          for(s=lastComp;*s && (*s == '\\' || *s == '/'); s++);
!     }
!     else
!         return 0;
  
      /* nulls, curdir and parent dir doesn't count */
!     if (!*s) 
!         return 0;
!     if (*s == '.') {
!         if (!*(s + 1)) 
!             return 0;
!         if(*(s+1) == '.' && !*(s + 2)) 
!             return 0;
!         return 1;
!     }
!     return 0;
  }
  
  static int ExtractBits(WORD bits, short start, short len)
  {
!     int end;
!     WORD num;
  
!     end = start + len;
          
!     num = bits << (16 - end);
!     num = num >> ((16 - end) + start);
  
!     return (int)num;
  }
  
  #ifndef DJGPP
  void ShowUnixTime(char *FuncName, time_t unixTime)
  {
!     FILETIME ft;
!     WORD wDate, wTime;
  
!     smb_LargeSearchTimeFromUnixTime(&ft, unixTime);
! 
!     if (!FileTimeToDosDateTime(&ft, &wDate, &wTime))
!         osi_Log1(smb_logp, "Failed to convert filetime to dos datetime: %d", GetLastError());
!     else {
!         int day, month, year, sec, min, hour;
!         char msg[256];
! 
!         day = ExtractBits(wDate, 0, 5);
!         month = ExtractBits(wDate, 5, 4);
!         year = ExtractBits(wDate, 9, 7) + 1980;
! 
!         sec = ExtractBits(wTime, 0, 5);
!         min = ExtractBits(wTime, 5, 6);
!         hour = ExtractBits(wTime, 11, 5);
! 
!         sprintf(msg, "%s = %02d-%02d-%04d %02d:%02d:%02d", FuncName, month, day, year, hour, min, sec);
!         osi_Log1(smb_logp, "%s", osi_LogSaveString(smb_logp, msg));
!     }
! }       
  #endif /* DJGPP */
  
  #ifndef DJGPP
  /* Determine if we are observing daylight savings time */
  void GetTimeZoneInfo(BOOL *pDST, LONG *pDstBias, LONG *pBias)
  {
!     TIME_ZONE_INFORMATION timeZoneInformation;
!     SYSTEMTIME utc, local, localDST;
  
!     /* Get the time zone info. NT uses this to calc if we are in DST. */
!     GetTimeZoneInformation(&timeZoneInformation);
  
!     /* Return the daylight bias */
!     *pDstBias = timeZoneInformation.DaylightBias;
  
!     /* Return the bias */
!     *pBias = timeZoneInformation.Bias;
  
!     /* Now determine if DST is being observed */
! 
!     /* Get the UTC (GMT) time */
!     GetSystemTime(&utc);
! 
!     /* Convert UTC time to local time using the time zone info.  If we are
!        observing DST, the calculated local time will include this. 
!      */
!     SystemTimeToTzSpecificLocalTime(&timeZoneInformation, &utc, &localDST);
! 
!     /* Set the daylight bias to 0.  The daylight bias is the amount of change
!      * in time that we use for daylight savings time.  By setting this to 0
!      * we cause there to be no change in time during daylight savings time. 
!      */
!     timeZoneInformation.DaylightBias = 0;
! 
!     /* Convert the utc time to local time again, but this time without any
!        adjustment for daylight savings time. 
!        */
!     SystemTimeToTzSpecificLocalTime(&timeZoneInformation, &utc, &local);
! 
!     /* If the two times are different, then it means that the localDST that
!        we calculated includes the daylight bias, and therefore we are
!        observing daylight savings time.
!      */
!     *pDST = localDST.wHour != local.wHour;
! }       
  #else
  /* Determine if we are observing daylight savings time */
  void GetTimeZoneInfo(BOOL *pDST, LONG *pDstBias, LONG *pBias)
  {
!     struct timeb t;
  
!     ftime(&t);
!     *pDST = t.dstflag;
!     *pDstBias = -60;    /* where can this be different? */
!     *pBias = t.timezone;
! }       
  #endif /* DJGPP */
   
  
  void CompensateForSmbClientLastWriteTimeBugs(long *pLastWriteTime)
  {
!     BOOL dst;       /* Will be TRUE if observing DST */
!     LONG dstBias;   /* Offset from local time if observing DST */
!     LONG bias;      /* Offset from GMT for local time */
! 
!     /*
!      * This function will adjust the last write time to compensate
!      * for two bugs in the smb client:
!      *
!      *    1) During Daylight Savings Time, the LastWriteTime is ahead
!      *       in time by the DaylightBias (ignoring the sign - the
!      *       DaylightBias is always stored as a negative number).  If
!      *       the DaylightBias is -60, then the LastWriteTime will be
!      *       ahead by 60 minutes.
!      *
!      *    2) If the local time zone is a positive offset from GMT, then
!      *       the LastWriteTime will be the correct local time plus the
!      *       Bias (ignoring the sign - a positive offset from GMT is
!      *       always stored as a negative Bias).  If the Bias is -120,
!      *       then the LastWriteTime will be ahead by 120 minutes.
!      *
!      *    These bugs can occur at the same time.
!      */
! 
!     GetTimeZoneInfo(&dst, &dstBias, &bias);
! 
!     /* First adjust for DST */
!     if (dst)
!         *pLastWriteTime -= (-dstBias * 60);     /* Convert dstBias to seconds */
! 
!     /* Now adjust for a positive offset from GMT (a negative bias). */
!     if (bias < 0)
!         *pLastWriteTime -= (-bias * 60);        /* Convert bias to seconds */
! }	        	
  
  /*
   * Calculate the difference (in seconds) between local time and GMT.
***************
*** 559,692 ****
  static void
  smb_CalculateNowTZ()
  {
! 	time_t t;
! 	struct tm gmt_tm, local_tm;
! 	int days, hours, minutes, seconds;
! 
! 	t = time(NULL);
! 	gmt_tm = *(gmtime(&t));
! 	local_tm = *(localtime(&t));
  
! 	days = local_tm.tm_yday - gmt_tm.tm_yday;
! 	hours = 24 * days + local_tm.tm_hour - gmt_tm.tm_hour
  #ifdef COMMENT
          /* There is a problem with DST immediately after the time change
!          * which may continue to exist until the machine is rebooted
           */
          - (local_tm.tm_isdst ? 1 : 0)
  #endif /* COMMENT */
!         ;
! 	minutes = 60 * hours + local_tm.tm_min - gmt_tm.tm_min;
! 	seconds = 60 * minutes + local_tm.tm_sec - gmt_tm.tm_sec;
  
! 	smb_NowTZ = seconds;
  }
  
  #ifndef DJGPP
  void smb_LargeSearchTimeFromUnixTime(FILETIME *largeTimep, time_t unixTime)
  {
! 	struct tm *ltp;
! 	SYSTEMTIME stm;
! 	struct tm localJunk;
! 	time_t ersatz_unixTime;
! 
! 	/*
! 	 * Must use kludge-GMT instead of real GMT.
! 	 * kludge-GMT is computed by adding time zone difference to localtime.
! 	 *
! 	 * real GMT would be:
! 	 * ltp = gmtime(&unixTime);
! 	 */
! 	ersatz_unixTime = unixTime - smb_NowTZ;
! 	ltp = localtime(&ersatz_unixTime);
! 
! 	/* if we fail, make up something */
! 	if (!ltp) {
! 		ltp = &localJunk;
! 		localJunk.tm_year = 89 - 20;
! 		localJunk.tm_mon = 4;
! 		localJunk.tm_mday = 12;
! 		localJunk.tm_hour = 0;
! 		localJunk.tm_min = 0;
! 		localJunk.tm_sec = 0;
! 	}
! 
! 	stm.wYear = ltp->tm_year + 1900;
! 	stm.wMonth = ltp->tm_mon + 1;
! 	stm.wDayOfWeek = ltp->tm_wday;
! 	stm.wDay = ltp->tm_mday;
! 	stm.wHour = ltp->tm_hour;
! 	stm.wMinute = ltp->tm_min;
! 	stm.wSecond = ltp->tm_sec;
! 	stm.wMilliseconds = 0;
  
! 	SystemTimeToFileTime(&stm, largeTimep);
  }
  #else /* DJGPP */
  void smb_LargeSearchTimeFromUnixTime(FILETIME *largeTimep, time_t unixTime)
  {
! 	/* unixTime: seconds since 1/1/1970 00:00:00 GMT */
! 	/* FILETIME: 100ns intervals since 1/1/1601 00:00:00 ??? */
! 	LARGE_INTEGER *ft = (LARGE_INTEGER *) largeTimep;
! 	LARGE_INTEGER ut;
! 	int leap_years = 89;   /* leap years betw 1/1/1601 and 1/1/1970 */
! 
! 	/* set ft to number of 100ns intervals betw 1/1/1601 and 1/1/1970 GMT */
! 	*ft = ConvertLongToLargeInteger(((EPOCH_YEAR-1601) * 365 + leap_years)
!                                    * 24 * 60);
! 	*ft = LargeIntegerMultiplyByLong(*ft, 60);
! 	*ft = LargeIntegerMultiplyByLong(*ft, 10000000);
! 
! 	/* add unix time */
! 	ut = ConvertLongToLargeInteger(unixTime);
! 	ut = LargeIntegerMultiplyByLong(ut, 10000000);
! 	*ft = LargeIntegerAdd(*ft, ut);
! }
  #endif /* !DJGPP */
  
  #ifndef DJGPP
  void smb_UnixTimeFromLargeSearchTime(time_t *unixTimep, FILETIME *largeTimep)
  {
! 	SYSTEMTIME stm;
! 	struct tm lt;
! 	long save_timezone;
! 
! 	FileTimeToSystemTime(largeTimep, &stm);
! 
! 	lt.tm_year = stm.wYear - 1900;
! 	lt.tm_mon = stm.wMonth - 1;
! 	lt.tm_wday = stm.wDayOfWeek;
! 	lt.tm_mday = stm.wDay;
! 	lt.tm_hour = stm.wHour;
! 	lt.tm_min = stm.wMinute;
! 	lt.tm_sec = stm.wSecond;
! 	lt.tm_isdst = -1;
! 
! 	save_timezone = _timezone;
! 	_timezone += smb_NowTZ;
! 	*unixTimep = mktime(&lt);
! 	_timezone = save_timezone;
! }
  #else /* DJGPP */
  void smb_UnixTimeFromLargeSearchTime(time_t *unixTimep, FILETIME *largeTimep)
  {
! 	/* unixTime: seconds since 1/1/1970 00:00:00 GMT */
! 	/* FILETIME: 100ns intervals since 1/1/1601 00:00:00 GMT? */
! 	LARGE_INTEGER *ft = (LARGE_INTEGER *) largeTimep;
! 	LARGE_INTEGER a;
! 	int leap_years = 89;
! 
! 	/* set to number of 100ns intervals betw 1/1/1601 and 1/1/1970 */
! 	a = ConvertLongToLargeInteger(((EPOCH_YEAR-1601) * 365 + leap_years) * 24 * 60);
! 	a = LargeIntegerMultiplyByLong(a, 60);
! 	a = LargeIntegerMultiplyByLong(a, 10000000);
! 
! 	/* subtract it from ft */
! 	a = LargeIntegerSubtract(*ft, a);
! 
! 	/* divide down to seconds */
! 	*unixTimep = LargeIntegerDivideByLong(a, 10000000);
! }
  #endif /* !DJGPP */
  
  void smb_SearchTimeFromUnixTime(long *dosTimep, time_t unixTime)
--- 572,705 ----
  static void
  smb_CalculateNowTZ()
  {
!     time_t t;
!     struct tm gmt_tm, local_tm;
!     int days, hours, minutes, seconds;
! 
!     t = time(NULL);
!     gmt_tm = *(gmtime(&t));
!     local_tm = *(localtime(&t));
  
!     days = local_tm.tm_yday - gmt_tm.tm_yday;
!     hours = 24 * days + local_tm.tm_hour - gmt_tm.tm_hour
  #ifdef COMMENT
          /* There is a problem with DST immediately after the time change
!         * which may continue to exist until the machine is rebooted
           */
          - (local_tm.tm_isdst ? 1 : 0)
  #endif /* COMMENT */
!             ;
!     minutes = 60 * hours + local_tm.tm_min - gmt_tm.tm_min; 
!     seconds = 60 * minutes + local_tm.tm_sec - gmt_tm.tm_sec;
  
!     smb_NowTZ = seconds;
  }
  
  #ifndef DJGPP
  void smb_LargeSearchTimeFromUnixTime(FILETIME *largeTimep, time_t unixTime)
  {
!     struct tm *ltp;
!     SYSTEMTIME stm;
!     struct tm localJunk;
!     time_t ersatz_unixTime;
! 
!     /*
!      * Must use kludge-GMT instead of real GMT.
!      * kludge-GMT is computed by adding time zone difference to localtime.
!      *
!      * real GMT would be:
!      * ltp = gmtime(&unixTime);
!      */
!     ersatz_unixTime = unixTime - smb_NowTZ;
!     ltp = localtime(&ersatz_unixTime);
! 
!     /* if we fail, make up something */
!     if (!ltp) {
!         ltp = &localJunk;
!         localJunk.tm_year = 89 - 20;
!         localJunk.tm_mon = 4;
!         localJunk.tm_mday = 12;
!         localJunk.tm_hour = 0;
!         localJunk.tm_min = 0;
!         localJunk.tm_sec = 0;
!     }
  
!     stm.wYear = ltp->tm_year + 1900;
!     stm.wMonth = ltp->tm_mon + 1;
!     stm.wDayOfWeek = ltp->tm_wday;
!     stm.wDay = ltp->tm_mday;
!     stm.wHour = ltp->tm_hour;
!     stm.wMinute = ltp->tm_min;
!     stm.wSecond = ltp->tm_sec;
!     stm.wMilliseconds = 0;
! 
!     SystemTimeToFileTime(&stm, largeTimep);
  }
  #else /* DJGPP */
  void smb_LargeSearchTimeFromUnixTime(FILETIME *largeTimep, time_t unixTime)
  {
!     /* unixTime: seconds since 1/1/1970 00:00:00 GMT */
!     /* FILETIME: 100ns intervals since 1/1/1601 00:00:00 ??? */
!     LARGE_INTEGER *ft = (LARGE_INTEGER *) largeTimep;
!     LARGE_INTEGER ut;
!     int leap_years = 89;   /* leap years betw 1/1/1601 and 1/1/1970 */
! 
!     /* set ft to number of 100ns intervals betw 1/1/1601 and 1/1/1970 GMT */
!     *ft = ConvertLongToLargeInteger(((EPOCH_YEAR-1601) * 365 + leap_years)
!                                      * 24 * 60);
!     *ft = LargeIntegerMultiplyByLong(*ft, 60);
!     *ft = LargeIntegerMultiplyByLong(*ft, 10000000);
! 
!     /* add unix time */
!     ut = ConvertLongToLargeInteger(unixTime);
!     ut = LargeIntegerMultiplyByLong(ut, 10000000);
!     *ft = LargeIntegerAdd(*ft, ut);
! }       
  #endif /* !DJGPP */
  
  #ifndef DJGPP
  void smb_UnixTimeFromLargeSearchTime(time_t *unixTimep, FILETIME *largeTimep)
  {
!     SYSTEMTIME stm;
!     struct tm lt;
!     long save_timezone;
! 
!     FileTimeToSystemTime(largeTimep, &stm);
! 
!     lt.tm_year = stm.wYear - 1900;
!     lt.tm_mon = stm.wMonth - 1;
!     lt.tm_wday = stm.wDayOfWeek;
!     lt.tm_mday = stm.wDay;
!     lt.tm_hour = stm.wHour;
!     lt.tm_min = stm.wMinute;
!     lt.tm_sec = stm.wSecond;
!     lt.tm_isdst = -1;
! 
!     save_timezone = _timezone;
!     _timezone += smb_NowTZ;
!     *unixTimep = mktime(&lt);
!     _timezone = save_timezone;
! }       
  #else /* DJGPP */
  void smb_UnixTimeFromLargeSearchTime(time_t *unixTimep, FILETIME *largeTimep)
  {
!     /* unixTime: seconds since 1/1/1970 00:00:00 GMT */
!     /* FILETIME: 100ns intervals since 1/1/1601 00:00:00 GMT? */
!     LARGE_INTEGER *ft = (LARGE_INTEGER *) largeTimep;
!     LARGE_INTEGER a;
!     int leap_years = 89;
! 
!     /* set to number of 100ns intervals betw 1/1/1601 and 1/1/1970 */
!     a = ConvertLongToLargeInteger(((EPOCH_YEAR-1601) * 365 + leap_years) * 24 * 60);
!     a = LargeIntegerMultiplyByLong(a, 60);
!     a = LargeIntegerMultiplyByLong(a, 10000000);
! 
!     /* subtract it from ft */
!     a = LargeIntegerSubtract(*ft, a);
! 
!     /* divide down to seconds */
!     *unixTimep = LargeIntegerDivideByLong(a, 10000000);
! }       
  #endif /* !DJGPP */
  
  void smb_SearchTimeFromUnixTime(long *dosTimep, time_t unixTime)
***************
*** 717,999 ****
  
  void smb_UnixTimeFromSearchTime(time_t *unixTimep, time_t searchTime)
  {
! 	unsigned short dosDate;
! 	unsigned short dosTime;
! 	struct tm localTm;
!         
! 	dosDate = searchTime & 0xffff;
! 	dosTime = (searchTime >> 16) & 0xffff;
!         
! 	localTm.tm_year = 80 + ((dosDate>>9) & 0x3f);
! 	localTm.tm_mon = ((dosDate >> 5) & 0xf) - 1;	/* January is 0 in localTm */
! 	localTm.tm_mday = (dosDate) & 0x1f;
! 	localTm.tm_hour = (dosTime>>11) & 0x1f;
! 	localTm.tm_min = (dosTime >> 5) & 0x3f;
! 	localTm.tm_sec = (dosTime & 0x1f) * 2;
! 	localTm.tm_isdst = -1;				/* compute whether DST in effect */
  
! 	*unixTimep = mktime(&localTm);
  }
  
  void smb_DosUTimeFromUnixTime(time_t *dosUTimep, time_t unixTime)
  {
! 	*dosUTimep = unixTime - smb_localZero;
  }
  
  void smb_UnixTimeFromDosUTime(time_t *unixTimep, time_t dosTime)
  {
  #ifndef DJGPP
! 	*unixTimep = dosTime + smb_localZero;
  #else /* DJGPP */
! 	/* dosTime seems to be already adjusted for GMT */
! 	*unixTimep = dosTime;
  #endif /* !DJGPP */
  }
  
  smb_vc_t *smb_FindVC(unsigned short lsn, int flags, int lana)
  {
! 	smb_vc_t *vcp;
  
! 	lock_ObtainWrite(&smb_rctLock);
! 	for(vcp = smb_allVCsp; vcp; vcp=vcp->nextp) {
! 		if (lsn == vcp->lsn && lana == vcp->lana) {
! 			vcp->refCount++;
! 			break;
! 		}
! 	}
! 	if (!vcp && (flags & SMB_FLAG_CREATE)) {
! 		vcp = malloc(sizeof(*vcp));
! 		memset(vcp, 0, sizeof(*vcp));
          vcp->vcID = numVCs++;
! 		vcp->refCount = 1;
! 		vcp->tidCounter = 1;
! 		vcp->fidCounter = 1;
! 		vcp->uidCounter = 1;  /* UID 0 is reserved for blank user */
! 		vcp->nextp = smb_allVCsp;
! 		smb_allVCsp = vcp;
! 		lock_InitializeMutex(&vcp->mx, "vc_t mutex");
! 		vcp->lsn = lsn;
! 		vcp->lana = lana;
          vcp->secCtx = NULL;
  
! 		if (smb_authType == SMB_AUTH_NTLM || smb_authType == SMB_AUTH_EXTENDED) {
              /* We must obtain a challenge for extended auth 
               * in case the client negotiates smb v3 
               */
              NTSTATUS nts,ntsEx;
! 			MSV1_0_LM20_CHALLENGE_REQUEST lsaReq;
! 			PMSV1_0_LM20_CHALLENGE_RESPONSE lsaResp;
! 			ULONG lsaRespSize;
  
! 			lsaReq.MessageType = MsV1_0Lm20ChallengeRequest;
  
! 			nts = LsaCallAuthenticationPackage( smb_lsaHandle,
                                                  smb_lsaSecPackage,
                                                  &lsaReq,
                                                  sizeof(lsaReq),
                                                  &lsaResp,
                                                  &lsaRespSize,
                                                  &ntsEx);
! 			osi_assert(nts == STATUS_SUCCESS); /* this had better work! */
  
! 			memcpy(vcp->encKey, lsaResp->ChallengeToClient, MSV1_0_CHALLENGE_LENGTH);
              LsaFreeReturnBuffer(lsaResp);
! 		}
! 		else
! 			memset(vcp->encKey, 0, MSV1_0_CHALLENGE_LENGTH);
! 	}
! 	lock_ReleaseWrite(&smb_rctLock);
! 	return vcp;
  }
  
  int smb_IsStarMask(char *maskp)
  {
! 	int i;
! 	char tc;
          
! 	for(i=0; i<11; i++) {
! 		tc = *maskp++;
! 		if (tc == '?' || tc == '*' || tc == '>') return 1;        
! 	}	
! 	return 0;
  }
  
  void smb_ReleaseVC(smb_vc_t *vcp)
  {
! 	lock_ObtainWrite(&smb_rctLock);
! 	osi_assert(vcp->refCount-- > 0);
! 	lock_ReleaseWrite(&smb_rctLock);
! }
  
  void smb_HoldVC(smb_vc_t *vcp)
  {
! 	lock_ObtainWrite(&smb_rctLock);
! 	vcp->refCount++;
! 	lock_ReleaseWrite(&smb_rctLock);
! }
  
  smb_tid_t *smb_FindTID(smb_vc_t *vcp, unsigned short tid, int flags)
  {
! 	smb_tid_t *tidp;
  
! 	lock_ObtainWrite(&smb_rctLock);
! 	for(tidp = vcp->tidsp; tidp; tidp = tidp->nextp) {
! 		if (tid == tidp->tid) {
! 			tidp->refCount++;
! 			break;
! 		}	
! 	}
! 	if (!tidp && (flags & SMB_FLAG_CREATE)) {
! 		tidp = malloc(sizeof(*tidp));
! 		memset(tidp, 0, sizeof(*tidp));
! 		tidp->nextp = vcp->tidsp;
! 		tidp->refCount = 1;
! 		tidp->vcp = vcp;
          vcp->refCount++;
! 		vcp->tidsp = tidp;
! 		lock_InitializeMutex(&tidp->mx, "tid_t mutex");
! 		tidp->tid = tid;
! 	}
! 	lock_ReleaseWrite(&smb_rctLock);
! 	return tidp;
! }	
  
  void smb_ReleaseTID(smb_tid_t *tidp)
  {
! 	smb_tid_t *tp;
! 	smb_tid_t **ltpp;
! 	cm_user_t *userp;
      smb_vc_t  *vcp;
  
! 	userp = NULL;
      vcp = NULL;
! 	lock_ObtainWrite(&smb_rctLock);
! 	osi_assert(tidp->refCount-- > 0);
! 	if (tidp->refCount == 0 && (tidp->flags & SMB_TIDFLAG_DELETE)) {
! 		ltpp = &tidp->vcp->tidsp;
! 		for(tp = *ltpp; tp; ltpp = &tp->nextp, tp = *ltpp) {
! 			if (tp == tidp) break;
! 		}
! 		osi_assert(tp != NULL);
! 		*ltpp = tp->nextp;
! 		lock_FinalizeMutex(&tidp->mx);
! 		userp = tidp->userp;	/* remember to drop ref later */
          vcp = tidp->vcp;
! 	}
! 	lock_ReleaseWrite(&smb_rctLock);
! 	if (userp) {
! 		cm_ReleaseUser(userp);
! 	}	
      if (vcp) {
          smb_ReleaseVC(vcp);
!     }
! }	
  
  smb_user_t *smb_FindUID(smb_vc_t *vcp, unsigned short uid, int flags)
  {
! 	smb_user_t *uidp = NULL;
  
! 	lock_ObtainWrite(&smb_rctLock);
! 	for(uidp = vcp->usersp; uidp; uidp = uidp->nextp) {
! 		if (uid == uidp->userID) {
! 			uidp->refCount++;
!                         osi_LogEvent("AFS smb_FindUID (Find by UID)",NULL," VCP[%x] found-uid[%d] name[%s]",
!                                       (int)vcp, uidp->userID, 
!                                       osi_LogSaveString(smb_logp, (uidp->unp) ? uidp->unp->name : ""));
!         	break;
! 		}
! 	}
! 	if (!uidp && (flags & SMB_FLAG_CREATE)) {
! 		uidp = malloc(sizeof(*uidp));
! 		memset(uidp, 0, sizeof(*uidp));
! 		uidp->nextp = vcp->usersp;
! 		uidp->refCount = 1;
! 		uidp->vcp = vcp;
          vcp->refCount++;
! 		vcp->usersp = uidp;
! 		lock_InitializeMutex(&uidp->mx, "user_t mutex");
! 		uidp->userID = uid;
! 		osi_LogEvent("AFS smb_FindUID (Find by UID)",NULL,"VCP[%x] new-uid[%d] name[%s]",(int)vcp,uidp->userID,(uidp->unp ? uidp->unp->name : ""));
! 	}
! 	lock_ReleaseWrite(&smb_rctLock);
! 	return uidp;
! }	
  
  smb_username_t *smb_FindUserByName(char *usern, char *machine, int flags)
  {
! 	smb_username_t *unp= NULL;
  
! 	lock_ObtainWrite(&smb_rctLock);
! 	for(unp = usernamesp; unp; unp = unp->nextp) {
! 		if (stricmp(unp->name, usern) == 0 &&
! 			stricmp(unp->machine, machine) == 0) {
! 			unp->refCount++;
! 			break;
! 		}
! 	}
! 	if (!unp && (flags & SMB_FLAG_CREATE)) {
! 		unp = malloc(sizeof(*unp));
! 		memset(unp, 0, sizeof(*unp));
! 		unp->refCount = 1;
! 		unp->nextp = usernamesp;
! 		unp->name = strdup(usern);
! 		unp->machine = strdup(machine);
! 		usernamesp = unp;
! 		lock_InitializeMutex(&unp->mx, "username_t mutex");
! 	}
! 	lock_ReleaseWrite(&smb_rctLock);
! 	return unp;
  }	
  
  smb_user_t *smb_FindUserByNameThisSession(smb_vc_t *vcp, char *usern)
  {
! 	smb_user_t *uidp= NULL;
  
! 	lock_ObtainWrite(&smb_rctLock);
! 	for(uidp = vcp->usersp; uidp; uidp = uidp->nextp) {
! 		if (!uidp->unp) 
              continue;
! 		if (stricmp(uidp->unp->name, usern) == 0) {
              uidp->refCount++;
! 			osi_LogEvent("AFS smb_FindUserByNameThisSession",NULL,"VCP[%x] uid[%d] match-name[%s]",(int)vcp,uidp->userID,usern);
              break;
! 		} else
              continue;
! 	}	
! 	lock_ReleaseWrite(&smb_rctLock);
! 	return uidp;
! }
  void smb_ReleaseUID(smb_user_t *uidp)
  {
! 	smb_user_t *up;
! 	smb_user_t **lupp;
! 	cm_user_t *userp;
      smb_vc_t  *vcp;
  
! 	userp = NULL;
      vcp = NULL;
! 	lock_ObtainWrite(&smb_rctLock);
! 	osi_assert(uidp->refCount-- > 0);
! 	if (uidp->refCount == 0 && (uidp->flags & SMB_USERFLAG_DELETE)) {
! 		lupp = &uidp->vcp->usersp;
! 		for(up = *lupp; up; lupp = &up->nextp, up = *lupp) {
! 			if (up == uidp) break;
! 		}
! 		osi_assert(up != NULL);
! 		*lupp = up->nextp;
! 		lock_FinalizeMutex(&uidp->mx);
! 		if (uidp->unp) {
! 			userp = uidp->unp->userp;	/* remember to drop ref later */
              uidp->unp->userp = NULL;
!         }
          vcp = uidp->vcp;
          uidp->vcp = NULL;
! 	}		
! 	lock_ReleaseWrite(&smb_rctLock);
! 	if (userp) {
! 		cm_ReleaseUserVCRef(userp);
! 		cm_ReleaseUser(userp);
! 	}	
      if (vcp) {
          smb_ReleaseVC(vcp);
      }
--- 730,1012 ----
  
  void smb_UnixTimeFromSearchTime(time_t *unixTimep, time_t searchTime)
  {
!     unsigned short dosDate;
!     unsigned short dosTime;
!     struct tm localTm;
!         
!     dosDate = searchTime & 0xffff;
!     dosTime = (searchTime >> 16) & 0xffff;
! 
!     localTm.tm_year = 80 + ((dosDate>>9) & 0x3f);
!     localTm.tm_mon = ((dosDate >> 5) & 0xf) - 1;	/* January is 0 in localTm */
!     localTm.tm_mday = (dosDate) & 0x1f;
!     localTm.tm_hour = (dosTime>>11) & 0x1f;
!     localTm.tm_min = (dosTime >> 5) & 0x3f;
!     localTm.tm_sec = (dosTime & 0x1f) * 2;
!     localTm.tm_isdst = -1;				/* compute whether DST in effect */
  
!     *unixTimep = mktime(&localTm);
  }
  
  void smb_DosUTimeFromUnixTime(time_t *dosUTimep, time_t unixTime)
  {
!     *dosUTimep = unixTime - smb_localZero;
  }
  
  void smb_UnixTimeFromDosUTime(time_t *unixTimep, time_t dosTime)
  {
  #ifndef DJGPP
!     *unixTimep = dosTime + smb_localZero;
  #else /* DJGPP */
!     /* dosTime seems to be already adjusted for GMT */
!     *unixTimep = dosTime;
  #endif /* !DJGPP */
  }
  
  smb_vc_t *smb_FindVC(unsigned short lsn, int flags, int lana)
  {
!     smb_vc_t *vcp;
  
!     lock_ObtainWrite(&smb_rctLock);
!     for(vcp = smb_allVCsp; vcp; vcp=vcp->nextp) {
!         if (lsn == vcp->lsn && lana == vcp->lana) {
!             vcp->refCount++;
!             break;
!         }
!     }
!     if (!vcp && (flags & SMB_FLAG_CREATE)) {
!         vcp = malloc(sizeof(*vcp));
!         memset(vcp, 0, sizeof(*vcp));
          vcp->vcID = numVCs++;
!         vcp->refCount = 1;
!         vcp->tidCounter = 1;
!         vcp->fidCounter = 1;
!         vcp->uidCounter = 1;  /* UID 0 is reserved for blank user */
!         vcp->nextp = smb_allVCsp;
!         smb_allVCsp = vcp;
!         lock_InitializeMutex(&vcp->mx, "vc_t mutex");
!         vcp->lsn = lsn;
!         vcp->lana = lana;
          vcp->secCtx = NULL;
  
!         if (smb_authType == SMB_AUTH_NTLM || smb_authType == SMB_AUTH_EXTENDED) {
              /* We must obtain a challenge for extended auth 
               * in case the client negotiates smb v3 
               */
              NTSTATUS nts,ntsEx;
!             MSV1_0_LM20_CHALLENGE_REQUEST lsaReq;
!             PMSV1_0_LM20_CHALLENGE_RESPONSE lsaResp;
!             ULONG lsaRespSize;
  
!             lsaReq.MessageType = MsV1_0Lm20ChallengeRequest;
  
!             nts = LsaCallAuthenticationPackage( smb_lsaHandle,
                                                  smb_lsaSecPackage,
                                                  &lsaReq,
                                                  sizeof(lsaReq),
                                                  &lsaResp,
                                                  &lsaRespSize,
                                                  &ntsEx);
!             osi_assert(nts == STATUS_SUCCESS); /* this had better work! */
  
!             memcpy(vcp->encKey, lsaResp->ChallengeToClient, MSV1_0_CHALLENGE_LENGTH);
              LsaFreeReturnBuffer(lsaResp);
!         }
!         else
!             memset(vcp->encKey, 0, MSV1_0_CHALLENGE_LENGTH);
!     }
!     lock_ReleaseWrite(&smb_rctLock);
!     return vcp;
  }
  
  int smb_IsStarMask(char *maskp)
  {
!     int i;
!     char tc;
          
!     for(i=0; i<11; i++) {
!         tc = *maskp++;
!         if (tc == '?' || tc == '*' || tc == '>') return 1;        
!     }	
!     return 0;
  }
  
  void smb_ReleaseVC(smb_vc_t *vcp)
  {
!     lock_ObtainWrite(&smb_rctLock);
!     osi_assert(vcp->refCount-- > 0);
!     lock_ReleaseWrite(&smb_rctLock);
! }       
  
  void smb_HoldVC(smb_vc_t *vcp)
  {
!     lock_ObtainWrite(&smb_rctLock);
!     vcp->refCount++;
!     lock_ReleaseWrite(&smb_rctLock);
! }       
  
  smb_tid_t *smb_FindTID(smb_vc_t *vcp, unsigned short tid, int flags)
  {
!     smb_tid_t *tidp;
  
!     lock_ObtainWrite(&smb_rctLock);
!     for (tidp = vcp->tidsp; tidp; tidp = tidp->nextp) {
!         if (tid == tidp->tid) {
!             tidp->refCount++;
!             break;
!         }	
!     }
!     if (!tidp && (flags & SMB_FLAG_CREATE)) {
!         tidp = malloc(sizeof(*tidp));
!         memset(tidp, 0, sizeof(*tidp));
!         tidp->nextp = vcp->tidsp;
!         tidp->refCount = 1;
!         tidp->vcp = vcp;
          vcp->refCount++;
!         vcp->tidsp = tidp;
!         lock_InitializeMutex(&tidp->mx, "tid_t mutex");
!         tidp->tid = tid;
!     }
!     lock_ReleaseWrite(&smb_rctLock);
!     return tidp;
! }       	
  
  void smb_ReleaseTID(smb_tid_t *tidp)
  {
!     smb_tid_t *tp;
!     smb_tid_t **ltpp;
!     cm_user_t *userp;
      smb_vc_t  *vcp;
  
!     userp = NULL;
      vcp = NULL;
!     lock_ObtainWrite(&smb_rctLock);
!     osi_assert(tidp->refCount-- > 0);
!     if (tidp->refCount == 0 && (tidp->flags & SMB_TIDFLAG_DELETE)) {
!         ltpp = &tidp->vcp->tidsp;
!         for(tp = *ltpp; tp; ltpp = &tp->nextp, tp = *ltpp) {
!             if (tp == tidp) break;
!         }
!         osi_assert(tp != NULL);
!         *ltpp = tp->nextp;
!         lock_FinalizeMutex(&tidp->mx);
!         userp = tidp->userp;	/* remember to drop ref later */
          vcp = tidp->vcp;
!     }
!     lock_ReleaseWrite(&smb_rctLock);
!     if (userp) {
!         cm_ReleaseUser(userp);
!     }	
      if (vcp) {
          smb_ReleaseVC(vcp);
!     }   
! }	        
  
  smb_user_t *smb_FindUID(smb_vc_t *vcp, unsigned short uid, int flags)
  {
!     smb_user_t *uidp = NULL;
  
!     lock_ObtainWrite(&smb_rctLock);
!     for(uidp = vcp->usersp; uidp; uidp = uidp->nextp) {
!         if (uid == uidp->userID) {
!             uidp->refCount++;
!             osi_LogEvent("AFS smb_FindUID (Find by UID)",NULL," VCP[%x] found-uid[%d] name[%s]",
!                           (int)vcp, uidp->userID, 
!                           osi_LogSaveString(smb_logp, (uidp->unp) ? uidp->unp->name : ""));
!             break;
!         }
!     }
!     if (!uidp && (flags & SMB_FLAG_CREATE)) {
!         uidp = malloc(sizeof(*uidp));
!         memset(uidp, 0, sizeof(*uidp));
!         uidp->nextp = vcp->usersp;
!         uidp->refCount = 1;
!         uidp->vcp = vcp;
          vcp->refCount++;
!         vcp->usersp = uidp;
!         lock_InitializeMutex(&uidp->mx, "user_t mutex");
!         uidp->userID = uid;
!         osi_LogEvent("AFS smb_FindUID (Find by UID)",NULL,"VCP[%x] new-uid[%d] name[%s]",(int)vcp,uidp->userID,(uidp->unp ? uidp->unp->name : ""));
!     }
!     lock_ReleaseWrite(&smb_rctLock);
!     return uidp;
! }       	
  
  smb_username_t *smb_FindUserByName(char *usern, char *machine, int flags)
  {
!     smb_username_t *unp= NULL;
  
!     lock_ObtainWrite(&smb_rctLock);
!     for(unp = usernamesp; unp; unp = unp->nextp) {
!         if (stricmp(unp->name, usern) == 0 &&
!              stricmp(unp->machine, machine) == 0) {
!             unp->refCount++;
!             break;
!         }
!     }
!     if (!unp && (flags & SMB_FLAG_CREATE)) {
!         unp = malloc(sizeof(*unp));
!         memset(unp, 0, sizeof(*unp));
!         unp->refCount = 1;
!         unp->nextp = usernamesp;
!         unp->name = strdup(usern);
!         unp->machine = strdup(machine);
!         usernamesp = unp;
!         lock_InitializeMutex(&unp->mx, "username_t mutex");
!     }
!     lock_ReleaseWrite(&smb_rctLock);
!     return unp;
  }	
  
  smb_user_t *smb_FindUserByNameThisSession(smb_vc_t *vcp, char *usern)
  {
!     smb_user_t *uidp= NULL;
  
!     lock_ObtainWrite(&smb_rctLock);
!     for(uidp = vcp->usersp; uidp; uidp = uidp->nextp) {
!         if (!uidp->unp) 
              continue;
!         if (stricmp(uidp->unp->name, usern) == 0) {
              uidp->refCount++;
!             osi_LogEvent("AFS smb_FindUserByNameThisSession",NULL,"VCP[%x] uid[%d] match-name[%s]",(int)vcp,uidp->userID,usern);
              break;
!         } else
              continue;
!     }       	
!     lock_ReleaseWrite(&smb_rctLock);
!     return uidp;
! }       
  void smb_ReleaseUID(smb_user_t *uidp)
  {
!     smb_user_t *up;
!     smb_user_t **lupp;
!     cm_user_t *userp;
      smb_vc_t  *vcp;
  
!     userp = NULL;
      vcp = NULL;
!     lock_ObtainWrite(&smb_rctLock);
!     osi_assert(uidp->refCount-- > 0);
!     if (uidp->refCount == 0 && (uidp->flags & SMB_USERFLAG_DELETE)) {
!         lupp = &uidp->vcp->usersp;
!         for(up = *lupp; up; lupp = &up->nextp, up = *lupp) {
!             if (up == uidp) break;
!         }
!         osi_assert(up != NULL);
!         *lupp = up->nextp;
!         lock_FinalizeMutex(&uidp->mx);
!         if (uidp->unp) {
!             userp = uidp->unp->userp;	/* remember to drop ref later */
              uidp->unp->userp = NULL;
!         }       
          vcp = uidp->vcp;
          uidp->vcp = NULL;
!     }		
!     lock_ReleaseWrite(&smb_rctLock);
!     if (userp) {
!         cm_ReleaseUserVCRef(userp);
!         cm_ReleaseUser(userp);
!     }	
      if (vcp) {
          smb_ReleaseVC(vcp);
      }
***************
*** 1005,1027 ****
   */
  cm_user_t *smb_GetUser(smb_vc_t *vcp, smb_packet_t *inp)
  {
! 	smb_user_t *uidp;
! 	cm_user_t *up;
! 	smb_t *smbp;
! 
! 	smbp = (smb_t *) inp;
! 	uidp = smb_FindUID(vcp, smbp->uid, 0);
! 	if ((!uidp) ||  (!uidp->unp))
! 		return NULL;
!         
! 	lock_ObtainMutex(&uidp->mx);
! 	up = uidp->unp->userp;
! 	cm_HoldUser(up);
! 	lock_ReleaseMutex(&uidp->mx);
  
! 	smb_ReleaseUID(uidp);
!         
! 	return up;
  }
  
  /*
--- 1018,1040 ----
   */
  cm_user_t *smb_GetUser(smb_vc_t *vcp, smb_packet_t *inp)
  {
!     smb_user_t *uidp;
!     cm_user_t *up;
!     smb_t *smbp;
  
!     smbp = (smb_t *) inp;
!     uidp = smb_FindUID(vcp, smbp->uid, 0);
!     if ((!uidp) ||  (!uidp->unp))
!         return NULL;
! 
!     lock_ObtainMutex(&uidp->mx);
!     up = uidp->unp->userp;
!     cm_HoldUser(up);
!     lock_ReleaseMutex(&uidp->mx);
! 
!     smb_ReleaseUID(uidp);
! 
!     return up;
  }
  
  /*
***************
*** 1030,1039 ****
   */
  long smb_LookupTIDPath(smb_vc_t *vcp, unsigned short tid, char ** treepath)
  {
! 	smb_tid_t *tidp;
      long code = 0;
  
! 	tidp = smb_FindTID(vcp, tid, 0);
      if (!tidp) {
          *treepath = NULL;
      } else {
--- 1043,1052 ----
   */
  long smb_LookupTIDPath(smb_vc_t *vcp, unsigned short tid, char ** treepath)
  {
!     smb_tid_t *tidp;
      long code = 0;
  
!     tidp = smb_FindTID(vcp, tid, 0);
      if (!tidp) {
          *treepath = NULL;
      } else {
***************
*** 1054,1069 ****
   */
  int smb_ChainFID(int fid, smb_packet_t *inp)
  {
! 	if (inp->fid == 0 || inp->inCount == 0) 
! 		return fid;
! 	else 
! 		return inp->fid;
  }
  
  /* are we a priv'd user?  What does this mean on NT? */
  int smb_SUser(cm_user_t *userp)
  {
! 	return 1;
  }
  
  /* find a file ID.  If we pass in 0 we select an used File ID.
--- 1067,1082 ----
   */
  int smb_ChainFID(int fid, smb_packet_t *inp)
  {
!     if (inp->fid == 0 || inp->inCount == 0) 
!         return fid;
!     else 
!         return inp->fid;
  }
  
  /* are we a priv'd user?  What does this mean on NT? */
  int smb_SUser(cm_user_t *userp)
  {
!     return 1;
  }
  
  /* find a file ID.  If we pass in 0 we select an used File ID.
***************
*** 1072,1102 ****
   */
  smb_fid_t *smb_FindFID(smb_vc_t *vcp, unsigned short fid, int flags)
  {
! 	smb_fid_t *fidp;
! 	int newFid = 0;
          
      if (fid == 0 && !(flags & SMB_FLAG_CREATE))
          return NULL;
  
! 	lock_ObtainWrite(&smb_rctLock);
! 	/* figure out if we need to allocate a new file ID */
! 	if (fid == 0) {
! 		newFid = 1;
! 		fid = vcp->fidCounter;
! 	}
! 
! retry:
! 	for(fidp = vcp->fidsp; fidp; fidp = (smb_fid_t *) osi_QNext(&fidp->q)) {
! 		if (fid == fidp->fid) {
! 			if (newFid) {
! 				fid++;
                  if (fid == 0) 
! 					fid = 1;
                  goto retry;
              }
! 			fidp->refCount++;
              break;
! 		}
      }
      if (!fidp && (flags & SMB_FLAG_CREATE)) {
          char eventName[MAX_PATH];
--- 1085,1115 ----
   */
  smb_fid_t *smb_FindFID(smb_vc_t *vcp, unsigned short fid, int flags)
  {
!     smb_fid_t *fidp;
!     int newFid = 0;
          
      if (fid == 0 && !(flags & SMB_FLAG_CREATE))
          return NULL;
  
!     lock_ObtainWrite(&smb_rctLock);
!     /* figure out if we need to allocate a new file ID */
!     if (fid == 0) {
!         newFid = 1;
!         fid = vcp->fidCounter;
!     }
! 
!   retry:
!     for(fidp = vcp->fidsp; fidp; fidp = (smb_fid_t *) osi_QNext(&fidp->q)) {
!         if (fid == fidp->fid) {
!             if (newFid) {
!                 fid++;
                  if (fid == 0) 
!                     fid = 1;
                  goto retry;
              }
!             fidp->refCount++;
              break;
!         }
      }
      if (!fidp && (flags & SMB_FLAG_CREATE)) {
          char eventName[MAX_PATH];
***************
*** 1112,1127 ****
              goto retry;
          }
  
! 		fidp = malloc(sizeof(*fidp));
          memset(fidp, 0, sizeof(*fidp));
! 		osi_QAdd((osi_queue_t **)&vcp->fidsp, &fidp->q);
          fidp->refCount = 1;
          fidp->vcp = vcp;
          vcp->refCount++;
          lock_InitializeMutex(&fidp->mx, "fid_t mutex");
          fidp->fid = fid;
! 		fidp->curr_chunk = fidp->prev_chunk = -2;
! 		fidp->raw_write_event = event;
          if (newFid) {
              vcp->fidCounter = fid+1;
              if (vcp->fidCounter == 0) 
--- 1125,1140 ----
              goto retry;
          }
  
!         fidp = malloc(sizeof(*fidp));
          memset(fidp, 0, sizeof(*fidp));
!         osi_QAdd((osi_queue_t **)&vcp->fidsp, &fidp->q);
          fidp->refCount = 1;
          fidp->vcp = vcp;
          vcp->refCount++;
          lock_InitializeMutex(&fidp->mx, "fid_t mutex");
          fidp->fid = fid;
!         fidp->curr_chunk = fidp->prev_chunk = -2;
!         fidp->raw_write_event = event;
          if (newFid) {
              vcp->fidCounter = fid+1;
              if (vcp->fidCounter == 0) 
***************
*** 1134,1176 ****
  
  void smb_ReleaseFID(smb_fid_t *fidp)
  {
! 	cm_scache_t *scp;
      smb_vc_t *vcp = NULL;
      smb_ioctl_t *ioctlp;
  
      if (!fidp)
          return;
  
! 	scp = NULL;
! 	lock_ObtainWrite(&smb_rctLock);
! 	osi_assert(fidp->refCount-- > 0);
      if (fidp->refCount == 0 && (fidp->flags & SMB_FID_DELETE)) {
! 		vcp = fidp->vcp;
! 		if (!(fidp->flags & SMB_FID_IOCTL))
! 			scp = fidp->scp;
! 		osi_QRemove((osi_queue_t **) &vcp->fidsp, &fidp->q);
! 		thrd_CloseHandle(fidp->raw_write_event);
  
! 		/* and see if there is ioctl stuff to free */
          ioctlp = fidp->ioctlp;
          if (ioctlp) {
! 			if (ioctlp->prefix) cm_FreeSpace(ioctlp->prefix);
! 			if (ioctlp->inAllocp) free(ioctlp->inAllocp);
! 			if (ioctlp->outAllocp) free(ioctlp->outAllocp);
! 			free(ioctlp);
!         }
  
          free(fidp);
  
          /* do not call smb_ReleaseVC() because we already have the lock */
          vcp->refCount--;
      }
! 	lock_ReleaseWrite(&smb_rctLock);
  
! 	/* now release the scache structure */
! 	if (scp) 
! 		cm_ReleaseSCache(scp);
! }
  
  /*
   * Case-insensitive search for one string in another;
--- 1147,1189 ----
  
  void smb_ReleaseFID(smb_fid_t *fidp)
  {
!     cm_scache_t *scp;
      smb_vc_t *vcp = NULL;
      smb_ioctl_t *ioctlp;
  
      if (!fidp)
          return;
  
!     scp = NULL;
!     lock_ObtainWrite(&smb_rctLock);
!     osi_assert(fidp->refCount-- > 0);
      if (fidp->refCount == 0 && (fidp->flags & SMB_FID_DELETE)) {
!         vcp = fidp->vcp;
!         if (!(fidp->flags & SMB_FID_IOCTL))
!             scp = fidp->scp;
!         osi_QRemove((osi_queue_t **) &vcp->fidsp, &fidp->q);
!         thrd_CloseHandle(fidp->raw_write_event);
  
!         /* and see if there is ioctl stuff to free */
          ioctlp = fidp->ioctlp;
          if (ioctlp) {
!             if (ioctlp->prefix) cm_FreeSpace(ioctlp->prefix);
!             if (ioctlp->inAllocp) free(ioctlp->inAllocp);
!             if (ioctlp->outAllocp) free(ioctlp->outAllocp);
!             free(ioctlp);
!         }       
  
          free(fidp);
  
          /* do not call smb_ReleaseVC() because we already have the lock */
          vcp->refCount--;
      }
!     lock_ReleaseWrite(&smb_rctLock);
  
!     /* now release the scache structure */
!     if (scp) 
!         cm_ReleaseSCache(scp);
! }       
  
  /*
   * Case-insensitive search for one string in another;
***************
*** 1178,1190 ****
   */
  static char *smb_stristr(char *str1, char *str2)
  {
! 	char *cursor;
  
! 	for (cursor = str1; *cursor; cursor++)
! 		if (stricmp(cursor, str2) == 0)
! 			return cursor;
  
! 	return NULL;
  }
  
  /*
--- 1191,1203 ----
   */
  static char *smb_stristr(char *str1, char *str2)
  {
!     char *cursor;
  
!     for (cursor = str1; *cursor; cursor++)
!         if (stricmp(cursor, str2) == 0)
!             return cursor;
  
!     return NULL;
  }
  
  /*
***************
*** 1193,1206 ****
   * length (plus one) is in substr_size.  Variable value is in newstr.
   */
  static void smb_subst(char *str1, char *substr, unsigned int substr_size,
! 	char *newstr)
  {
! 	char temp[1024];
  
! 	strcpy(temp, substr + substr_size - 1);
! 	strcpy(substr, newstr);
! 	strcat(str1, temp);
! }
  
  char VNUserName[] = "%USERNAME%";
  char VNLCUserName[] = "%LCUSERNAME%";
--- 1206,1219 ----
   * length (plus one) is in substr_size.  Variable value is in newstr.
   */
  static void smb_subst(char *str1, char *substr, unsigned int substr_size,
!                       char *newstr)
  {
!     char temp[1024];
  
!     strcpy(temp, substr + substr_size - 1);
!     strcpy(substr, newstr);
!     strcat(str1, temp);
! }       
  
  char VNUserName[] = "%USERNAME%";
  char VNLCUserName[] = "%LCUSERNAME%";
***************
*** 1211,1275 ****
  /* List available shares */
  int smb_ListShares()
  {
! 	char sbmtpath[256];
! 	char pathName[256];
! 	char shareBuf[4096];
! 	int num_shares=0;
! 	char *this_share;
! 	int len;
! 	char *p;
! 	int print_afs = 0;
! 	int code;
! 
! 	/*strcpy(shareNameList[num_shares], "all");
! 	 strcpy(pathNameList[num_shares++], "/afs");*/
! 	fprintf(stderr, "The following shares are available:\n");
! 	fprintf(stderr, "Share Name (AFS Path)\n");
! 	fprintf(stderr, "---------------------\n");
! 	fprintf(stderr, "\\\\%s\\%-16s (%s)\n", smb_localNamep, "ALL", cm_mountRoot);
  
  #ifndef DJGPP
! 	code = GetWindowsDirectory(sbmtpath, sizeof(sbmtpath));
! 	if (code == 0 || code > sizeof(sbmtpath)) return -1;
  #else
! 	strcpy(sbmtpath, cm_confDir);
  #endif /* !DJGPP */
! 	strcat(sbmtpath, "/afsdsbmt.ini");
! 	len = GetPrivateProfileString("AFS Submounts", NULL, NULL,
! 								   shareBuf, sizeof(shareBuf),
! 								   sbmtpath);
! 	if (len == 0) {
! 		return num_shares;
! 	}
! 
! 	this_share = shareBuf;
! 	do
! 	{
! 		print_afs = 0;
! 		/*strcpy(shareNameList[num_shares], this_share);*/
! 		len = GetPrivateProfileString("AFS Submounts", this_share,
! 									   NULL,
! 									   pathName, 256,
! 									   sbmtpath);
! 		if (!len) 
! 			return num_shares;
! 		p = pathName;
! 		if (strncmp(p, cm_mountRoot, strlen(cm_mountRoot)) != 0)
              print_afs = 1;
! 		while (*p) {
              if (*p == '\\') *p = '/';    /* change to / */
              p++;
! 		}
  
! 		fprintf(stderr, "\\\\%s\\%-16s (%s%s)\n",
! 				 smb_localNamep, this_share, (print_afs ? cm_mountRoot : "\0"),
! 				 pathName);
! 		num_shares++;
! 		while (*this_share != 0) this_share++;  /* find next NUL */
! 		this_share++;   /* skip past the NUL */
! 	} while (*this_share != 0);  /* stop at final NUL */
  
! 	return num_shares;
  }
  #endif /* DJGPP */
  
--- 1224,1288 ----
  /* List available shares */
  int smb_ListShares()
  {
!     char sbmtpath[256];
!     char pathName[256];
!     char shareBuf[4096];
!     int num_shares=0;
!     char *this_share;
!     int len;
!     char *p;
!     int print_afs = 0;
!     int code;
! 
!     /*strcpy(shareNameList[num_shares], "all");
!       strcpy(pathNameList[num_shares++], "/afs");*/
!     fprintf(stderr, "The following shares are available:\n");
!     fprintf(stderr, "Share Name (AFS Path)\n");
!     fprintf(stderr, "---------------------\n");
!     fprintf(stderr, "\\\\%s\\%-16s (%s)\n", smb_localNamep, "ALL", cm_mountRoot);
  
  #ifndef DJGPP
!     code = GetWindowsDirectory(sbmtpath, sizeof(sbmtpath));
!     if (code == 0 || code > sizeof(sbmtpath)) return -1;
  #else
!     strcpy(sbmtpath, cm_confDir);
  #endif /* !DJGPP */
!     strcat(sbmtpath, "/afsdsbmt.ini");
!     len = GetPrivateProfileString("AFS Submounts", NULL, NULL,
!                                    shareBuf, sizeof(shareBuf),
!                                    sbmtpath);
!     if (len == 0) {
!         return num_shares;
!     }
! 
!     this_share = shareBuf;
!     do
!     {
!         print_afs = 0;
!         /*strcpy(shareNameList[num_shares], this_share);*/
!         len = GetPrivateProfileString("AFS Submounts", this_share,
!                                        NULL,
!                                        pathName, 256,
!                                        sbmtpath);
!         if (!len) 
!             return num_shares;
!         p = pathName;
!         if (strncmp(p, cm_mountRoot, strlen(cm_mountRoot)) != 0)
              print_afs = 1;
!         while (*p) {
              if (*p == '\\') *p = '/';    /* change to / */
              p++;
!         }
  
!         fprintf(stderr, "\\\\%s\\%-16s (%s%s)\n",
!                  smb_localNamep, this_share, (print_afs ? cm_mountRoot : "\0"),
!                  pathName);
!         num_shares++;
!         while (*this_share != 0) this_share++;  /* find next NUL */
!         this_share++;   /* skip past the NUL */
!     } while (*this_share != 0);  /* stop at final NUL */
  
!     return num_shares;
  }
  #endif /* DJGPP */
  
***************
*** 1283,1289 ****
  #define SMB_FINDSHARE_PARTIAL_MATCH 2
  
  long smb_FindShareProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
! 	osi_hyper_t *offp)
  {
      int matchType = 0;
      smb_findShare_rock_t * vrock = (smb_findShare_rock_t *) rockp;
--- 1296,1302 ----
  #define SMB_FINDSHARE_PARTIAL_MATCH 2
  
  long smb_FindShareProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
!                        osi_hyper_t *offp)
  {
      int matchType = 0;
      smb_findShare_rock_t * vrock = (smb_findShare_rock_t *) rockp;
***************
*** 1307,1323 ****
  int smb_FindShare(smb_vc_t *vcp, smb_user_t *uidp, char *shareName,
  	char **pathNamep)
  {
! 	DWORD len;
! 	char pathName[1024];
! 	char *var;
! 	char temp[1024];
! 	DWORD sizeTemp;
  #ifdef DJGPP
      char sbmtpath[MAX_PATH];
  #endif
      char *p, *q;
! 	HKEY parmKey;
! 	DWORD code;
      DWORD allSubmount = 1;
  
      /* if allSubmounts == 0, only return the //mountRoot/all share 
--- 1320,1336 ----
  int smb_FindShare(smb_vc_t *vcp, smb_user_t *uidp, char *shareName,
  	char **pathNamep)
  {
!     DWORD len;
!     char pathName[1024];
!     char *var;
!     char temp[1024];
!     DWORD sizeTemp;
  #ifdef DJGPP
      char sbmtpath[MAX_PATH];
  #endif
      char *p, *q;
!     HKEY parmKey;
!     DWORD code;
      DWORD allSubmount = 1;
  
      /* if allSubmounts == 0, only return the //mountRoot/all share 
***************
*** 1325,1333 ****
       * This is to allow sites that want to restrict access to the 
       * world to do so.
       */
! 	code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSConfigKeyName,
! 						0, KEY_QUERY_VALUE, &parmKey);
! 	if (code == ERROR_SUCCESS) {
          len = sizeof(allSubmount);
          code = RegQueryValueEx(parmKey, "AllSubmount", NULL, NULL,
                                  (BYTE *) &allSubmount, &len);
--- 1338,1346 ----
       * This is to allow sites that want to restrict access to the 
       * world to do so.
       */
!     code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSConfigKeyName,
!                          0, KEY_QUERY_VALUE, &parmKey);
!     if (code == ERROR_SUCCESS) {
          len = sizeof(allSubmount);
          code = RegQueryValueEx(parmKey, "AllSubmount", NULL, NULL,
                                  (BYTE *) &allSubmount, &len);
***************
*** 1335,1383 ****
              allSubmount = 1;
          }
          RegCloseKey (parmKey);
! 	}
  
! 	if (allSubmount && _stricmp(shareName, "all") == 0) {
! 		*pathNamep = NULL;
! 		return 1;
! 	}
  
      /* In case, the all share is disabled we need to still be able
       * to handle ioctl requests 
       */
! 	if (_stricmp(shareName, "ioctl$") == 0) {
! 		*pathNamep = strdup("/.__ioctl__");
! 		return 1;
! 	}
  
      if (_stricmp(shareName, "IPC$") == 0 ||
          _stricmp(shareName, SMB_IOCTL_FILENAME_NOSLASH) == 0 ||
          _stricmp(shareName, "DESKTOP.INI") == 0
           ) {
! 		*pathNamep = NULL;
! 		return 0;
! 	}
  
  #ifndef DJGPP
! 	code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\OpenAFS\\Client\\Submounts",
! 						0, KEY_QUERY_VALUE, &parmKey);
! 	if (code == ERROR_SUCCESS) {
          len = sizeof(pathName);
          code = RegQueryValueEx(parmKey, shareName, NULL, NULL,
                                  (BYTE *) pathName, &len);
! 		if (code != ERROR_SUCCESS)
! 			len = 0;
          RegCloseKey (parmKey);
! 	} else {
          len = 0;
      }   
  #else /* DJGPP */
      strcpy(sbmtpath, cm_confDir);
      strcat(sbmtpath, "/afsdsbmt.ini");
! 	len = GetPrivateProfileString("AFS Submounts", shareName, "",
!                                   pathName, sizeof(pathName), sbmtpath);
  #endif /* !DJGPP */
! 	if (len != 0 && len != sizeof(pathName) - 1) {
          /* We can accept either unix or PC style AFS pathnames.  Convert
           * Unix-style to PC style here for internal use. 
           */
--- 1348,1396 ----
              allSubmount = 1;
          }
          RegCloseKey (parmKey);
!     }
  
!     if (allSubmount && _stricmp(shareName, "all") == 0) {
!         *pathNamep = NULL;
!         return 1;
!     }
  
      /* In case, the all share is disabled we need to still be able
       * to handle ioctl requests 
       */
!     if (_stricmp(shareName, "ioctl$") == 0) {
!         *pathNamep = strdup("/.__ioctl__");
!         return 1;
!     }
  
      if (_stricmp(shareName, "IPC$") == 0 ||
          _stricmp(shareName, SMB_IOCTL_FILENAME_NOSLASH) == 0 ||
          _stricmp(shareName, "DESKTOP.INI") == 0
           ) {
!         *pathNamep = NULL;
!         return 0;
!     }
  
  #ifndef DJGPP
!     code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\OpenAFS\\Client\\Submounts",
!                          0, KEY_QUERY_VALUE, &parmKey);
!     if (code == ERROR_SUCCESS) {
          len = sizeof(pathName);
          code = RegQueryValueEx(parmKey, shareName, NULL, NULL,
                                  (BYTE *) pathName, &len);
!         if (code != ERROR_SUCCESS)
!             len = 0;
          RegCloseKey (parmKey);
!     } else {
          len = 0;
      }   
  #else /* DJGPP */
      strcpy(sbmtpath, cm_confDir);
      strcat(sbmtpath, "/afsdsbmt.ini");
!     len = GetPrivateProfileString("AFS Submounts", shareName, "",
!                                    pathName, sizeof(pathName), sbmtpath);
  #endif /* !DJGPP */
!     if (len != 0 && len != sizeof(pathName) - 1) {
          /* We can accept either unix or PC style AFS pathnames.  Convert
           * Unix-style to PC style here for internal use. 
           */
***************
*** 1478,1484 ****
              *pathNamep = strdup(strlwr(pathName));
              return 1;
          }
! 	}
      /* failure */
      *pathNamep = NULL;
      return 0;
--- 1491,1497 ----
              *pathNamep = strdup(strlwr(pathName));
              return 1;
          }
!     }
      /* failure */
      *pathNamep = NULL;
      return 0;
***************
*** 1492,1499 ****
  
  int smb_FindShareCSCPolicy(char *shareName)
  {
! 	DWORD len;
! 	char policy[1024];
      DWORD dwType;
      HKEY hkCSCPolicy;
      int  retval = CSC_POLICY_MANUAL;
--- 1505,1512 ----
  
  int smb_FindShareCSCPolicy(char *shareName)
  {
!     DWORD len;
!     char policy[1024];
      DWORD dwType;
      HKEY hkCSCPolicy;
      int  retval = CSC_POLICY_MANUAL;
***************
*** 1511,1533 ****
      len = sizeof(policy);
      if ( RegQueryValueEx( hkCSCPolicy, shareName, 0, &dwType, policy, &len ) ||
           len == 0) {
! 		retval = CSC_POLICY_MANUAL;
      }
- 	else if (stricmp(policy, "documents") == 0)
- 	{
- 		retval = CSC_POLICY_DOCUMENTS;
- 	}
- 	else if (stricmp(policy, "programs") == 0)
- 	{
- 		retval = CSC_POLICY_PROGRAMS;
- 	}
- 	else if (stricmp(policy, "disable") == 0)
- 	{
- 		retval = CSC_POLICY_DISABLE;
- 	}
  	
      RegCloseKey(hkCSCPolicy);
! 	return retval;
  }
  
  /* find a dir search structure by cookie value, and return it held.
--- 1524,1546 ----
      len = sizeof(policy);
      if ( RegQueryValueEx( hkCSCPolicy, shareName, 0, &dwType, policy, &len ) ||
           len == 0) {
!         retval = stricmp("all",shareName) ? CSC_POLICY_MANUAL : CSC_POLICY_DISABLE;
!     }
!     else if (stricmp(policy, "documents") == 0)
!     {
!         retval = CSC_POLICY_DOCUMENTS;
!     }
!     else if (stricmp(policy, "programs") == 0)
!     {
!         retval = CSC_POLICY_PROGRAMS;
!     }
!     else if (stricmp(policy, "disable") == 0)
!     {
!         retval = CSC_POLICY_DISABLE;
      }
  	
      RegCloseKey(hkCSCPolicy);
!     return retval;
  }
  
  /* find a dir search structure by cookie value, and return it held.
***************
*** 1535,1610 ****
   */
  smb_dirSearch_t *smb_FindDirSearchNL(long cookie)
  {
! 	smb_dirSearch_t *dsp;
          
! 	for(dsp = smb_firstDirSearchp; dsp; dsp = (smb_dirSearch_t *) osi_QNext(&dsp->q)) {
! 		if (dsp->cookie == cookie) {
! 			if (dsp != smb_firstDirSearchp) {
! 				/* move to head of LRU queue, too, if we're not already there */
! 				if (smb_lastDirSearchp == (smb_dirSearch_t *) &dsp->q)
! 					smb_lastDirSearchp = (smb_dirSearch_t *)
! 						osi_QPrev(&dsp->q);
! 				osi_QRemove((osi_queue_t **) &smb_firstDirSearchp, &dsp->q);
! 				osi_QAdd((osi_queue_t **) &smb_firstDirSearchp, &dsp->q);
! 				if (!smb_lastDirSearchp)
! 					smb_lastDirSearchp = (smb_dirSearch_t *) &dsp->q;
! 			}
! 			dsp->refCount++;
! 			break;
! 		}
! 	}
! 	return dsp;
! }
  
  void smb_DeleteDirSearch(smb_dirSearch_t *dsp)
  {
! 	lock_ObtainWrite(&smb_globalLock);
! 	dsp->flags |= SMB_DIRSEARCH_DELETE;
! 	lock_ReleaseWrite(&smb_globalLock);
! 	lock_ObtainMutex(&dsp->mx);
! 	if(dsp->scp != NULL) {
! 		lock_ObtainMutex(&dsp->scp->mx);
! 		if (dsp->flags & SMB_DIRSEARCH_BULKST) {
! 			dsp->flags &= ~SMB_DIRSEARCH_BULKST;
! 		    dsp->scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
! 		    dsp->scp->bulkStatProgress = hones;
! 		}	
! 		lock_ReleaseMutex(&dsp->scp->mx);
! 	}	
! 	lock_ReleaseMutex(&dsp->mx);
! }
  
  void smb_ReleaseDirSearch(smb_dirSearch_t *dsp)
  {
! 	cm_scache_t *scp;
          
! 	scp = NULL;
  
! 	lock_ObtainWrite(&smb_globalLock);
! 	osi_assert(dsp->refCount-- > 0);
! 	if (dsp->refCount == 0 && (dsp->flags & SMB_DIRSEARCH_DELETE)) {
! 		if (&dsp->q == (osi_queue_t *) smb_lastDirSearchp)
! 			smb_lastDirSearchp = (smb_dirSearch_t *) osi_QPrev(&smb_lastDirSearchp->q);
! 		osi_QRemove((osi_queue_t **) &smb_firstDirSearchp, &dsp->q);
! 		lock_FinalizeMutex(&dsp->mx);
! 		scp = dsp->scp;
! 		free(dsp);
! 	}
! 	lock_ReleaseWrite(&smb_globalLock);
  
! 	/* do this now to avoid spurious locking hierarchy creation */
! 	if (scp) cm_ReleaseSCache(scp);
! }
  
  /* find a dir search structure by cookie value, and return it held */
  smb_dirSearch_t *smb_FindDirSearch(long cookie)
  {
! 	smb_dirSearch_t *dsp;
  
! 	lock_ObtainWrite(&smb_globalLock);
! 	dsp = smb_FindDirSearchNL(cookie);
! 	lock_ReleaseWrite(&smb_globalLock);
! 	return dsp;
  }
  
  /* GC some dir search entries, in the address space expected by the specific protocol.
--- 1548,1623 ----
   */
  smb_dirSearch_t *smb_FindDirSearchNL(long cookie)
  {
!     smb_dirSearch_t *dsp;
          
!     for(dsp = smb_firstDirSearchp; dsp; dsp = (smb_dirSearch_t *) osi_QNext(&dsp->q)) {
!         if (dsp->cookie == cookie) {
!             if (dsp != smb_firstDirSearchp) {
!                 /* move to head of LRU queue, too, if we're not already there */
!                 if (smb_lastDirSearchp == (smb_dirSearch_t *) &dsp->q)
!                     smb_lastDirSearchp = (smb_dirSearch_t *)
!                         osi_QPrev(&dsp->q);
!                 osi_QRemove((osi_queue_t **) &smb_firstDirSearchp, &dsp->q);
!                 osi_QAdd((osi_queue_t **) &smb_firstDirSearchp, &dsp->q);
!                 if (!smb_lastDirSearchp)
!                     smb_lastDirSearchp = (smb_dirSearch_t *) &dsp->q;
!             }
!             dsp->refCount++;
!             break;
!         }
!     }
!     return dsp;
! }       
  
  void smb_DeleteDirSearch(smb_dirSearch_t *dsp)
  {
!     lock_ObtainWrite(&smb_globalLock);
!     dsp->flags |= SMB_DIRSEARCH_DELETE;
!     lock_ReleaseWrite(&smb_globalLock);
!     lock_ObtainMutex(&dsp->mx);
!     if(dsp->scp != NULL) {
!         lock_ObtainMutex(&dsp->scp->mx);
!         if (dsp->flags & SMB_DIRSEARCH_BULKST) {
!             dsp->flags &= ~SMB_DIRSEARCH_BULKST;
!             dsp->scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
!             dsp->scp->bulkStatProgress = hones;
!         }	
!         lock_ReleaseMutex(&dsp->scp->mx);
!     }	
!     lock_ReleaseMutex(&dsp->mx);
! }               
  
  void smb_ReleaseDirSearch(smb_dirSearch_t *dsp)
  {
!     cm_scache_t *scp;
          
!     scp = NULL;
  
!     lock_ObtainWrite(&smb_globalLock);
!     osi_assert(dsp->refCount-- > 0);
!     if (dsp->refCount == 0 && (dsp->flags & SMB_DIRSEARCH_DELETE)) {
!         if (&dsp->q == (osi_queue_t *) smb_lastDirSearchp)
!             smb_lastDirSearchp = (smb_dirSearch_t *) osi_QPrev(&smb_lastDirSearchp->q);
!         osi_QRemove((osi_queue_t **) &smb_firstDirSearchp, &dsp->q);
!         lock_FinalizeMutex(&dsp->mx);
!         scp = dsp->scp;
!         free(dsp);
!     }
!     lock_ReleaseWrite(&smb_globalLock);
  
!     /* do this now to avoid spurious locking hierarchy creation */
!     if (scp) cm_ReleaseSCache(scp);
! }       
  
  /* find a dir search structure by cookie value, and return it held */
  smb_dirSearch_t *smb_FindDirSearch(long cookie)
  {
!     smb_dirSearch_t *dsp;
  
!     lock_ObtainWrite(&smb_globalLock);
!     dsp = smb_FindDirSearchNL(cookie);
!     lock_ReleaseWrite(&smb_globalLock);
!     return dsp;
  }
  
  /* GC some dir search entries, in the address space expected by the specific protocol.
***************
*** 1613,1651 ****
  #define SMB_DIRSEARCH_GCMAX	10	/* how many at once */
  void smb_GCDirSearches(int isV3)
  {
! 	smb_dirSearch_t *prevp;
! 	smb_dirSearch_t *tp;
! 	smb_dirSearch_t *victimsp[SMB_DIRSEARCH_GCMAX];
! 	int victimCount;
! 	int i;
!         
! 	victimCount = 0;	/* how many have we got so far */
! 	for(tp = smb_lastDirSearchp; tp; tp=prevp) {
! 		/* we'll move tp from queue, so
! 		 * do this early.
! 		 */
! 		prevp = (smb_dirSearch_t *) osi_QPrev(&tp->q);	
! 		/* if no one is using this guy, and we're either in the new protocol,
! 		 * or we're in the old one and this is a small enough ID to be useful
! 		 * to the old protocol, GC this guy.
! 		 */
! 		if (tp->refCount == 0 && (isV3 || tp->cookie <= 255)) {
! 			/* hold and delete */
! 			tp->flags |= SMB_DIRSEARCH_DELETE;
! 			victimsp[victimCount++] = tp;
! 			tp->refCount++;
! 		}
! 
! 		/* don't do more than this */
! 		if (victimCount >= SMB_DIRSEARCH_GCMAX) break;
! 	}
  	
! 	/* now release them */
! 	lock_ReleaseWrite(&smb_globalLock);
! 	for(i = 0; i < victimCount; i++) {
! 		smb_ReleaseDirSearch(victimsp[i]);
! 	}
! 	lock_ObtainWrite(&smb_globalLock);
  }
  
  /* function for allocating a dir search entry.  We need these to remember enough context
--- 1626,1664 ----
  #define SMB_DIRSEARCH_GCMAX	10	/* how many at once */
  void smb_GCDirSearches(int isV3)
  {
!     smb_dirSearch_t *prevp;
!     smb_dirSearch_t *tp;
!     smb_dirSearch_t *victimsp[SMB_DIRSEARCH_GCMAX];
!     int victimCount;
!     int i;
!         
!     victimCount = 0;	/* how many have we got so far */
!     for(tp = smb_lastDirSearchp; tp; tp=prevp) {
!         /* we'll move tp from queue, so
!          * do this early.
!          */
!         prevp = (smb_dirSearch_t *) osi_QPrev(&tp->q);	
!         /* if no one is using this guy, and we're either in the new protocol,
!          * or we're in the old one and this is a small enough ID to be useful
!          * to the old protocol, GC this guy.
!          */
!         if (tp->refCount == 0 && (isV3 || tp->cookie <= 255)) {
!             /* hold and delete */
!             tp->flags |= SMB_DIRSEARCH_DELETE;
!             victimsp[victimCount++] = tp;
!             tp->refCount++;
!         }
! 
!         /* don't do more than this */
!         if (victimCount >= SMB_DIRSEARCH_GCMAX) break;
!     }
  	
!     /* now release them */
!     lock_ReleaseWrite(&smb_globalLock);
!     for(i = 0; i < victimCount; i++) {
!         smb_ReleaseDirSearch(victimsp[i]);
!     }
!     lock_ObtainWrite(&smb_globalLock);
  }
  
  /* function for allocating a dir search entry.  We need these to remember enough context
***************
*** 1656,1719 ****
   */
  smb_dirSearch_t *smb_NewDirSearch(int isV3)
  {
! 	smb_dirSearch_t *dsp;
! 	int counter;
! 	int maxAllowed;
! 
! 	lock_ObtainWrite(&smb_globalLock);
! 	counter = 0;
! 
! 	/* what's the biggest ID allowed in this version of the protocol */
! 	if (isV3) maxAllowed = 65535;
! 	else maxAllowed = 255;
! 
! 	while(1) {
! 		/* twice so we have enough tries to find guys we GC after one pass;
! 		 * 10 extra is just in case I mis-counted.
! 		 */
! 		if (++counter > 2*maxAllowed+10) osi_panic("afsd: dir search cookie leak",
! 													__FILE__, __LINE__);
! 		if (smb_dirSearchCounter > maxAllowed) {	
! 			smb_dirSearchCounter = 1;
! 			smb_GCDirSearches(isV3);	/* GC some (drops global lock) */
! 		}	
! 		dsp = smb_FindDirSearchNL(smb_dirSearchCounter);
! 		if (dsp) {
! 			/* don't need to watch for refcount zero and deleted, since
! 			 * we haven't dropped the global lock.
! 			 */
! 			dsp->refCount--;
! 			++smb_dirSearchCounter;
! 			continue;
! 		}	
!                 
! 		dsp = malloc(sizeof(*dsp));
! 		memset(dsp, 0, sizeof(*dsp));
! 		osi_QAdd((osi_queue_t **) &smb_firstDirSearchp, &dsp->q);
! 		if (!smb_lastDirSearchp) smb_lastDirSearchp = (smb_dirSearch_t *) &dsp->q;
! 		dsp->cookie = smb_dirSearchCounter;
! 		++smb_dirSearchCounter;
! 		dsp->refCount = 1;
! 		lock_InitializeMutex(&dsp->mx, "cm_dirSearch_t");
! 		dsp->lastTime = osi_Time();
! 		break;
! 	}	
! 	lock_ReleaseWrite(&smb_globalLock);
! 	return dsp;
  }
  
  static smb_packet_t *GetPacket(void)
  {
! 	smb_packet_t *tbp;
  #ifdef DJGPP
!         unsigned int npar, seg, tb_sel;
  #endif
  
! 	lock_ObtainWrite(&smb_globalLock);
! 	tbp = smb_packetFreeListp;
      if (tbp) 
          smb_packetFreeListp = tbp->nextp;
! 	lock_ReleaseWrite(&smb_globalLock);
      if (!tbp) {
  #ifndef DJGPP
          tbp = calloc(65540,1);
--- 1669,1732 ----
   */
  smb_dirSearch_t *smb_NewDirSearch(int isV3)
  {
!     smb_dirSearch_t *dsp;
!     int counter;
!     int maxAllowed;
! 
!     lock_ObtainWrite(&smb_globalLock);
!     counter = 0;
! 
!     /* what's the biggest ID allowed in this version of the protocol */
!     if (isV3) maxAllowed = 65535;
!     else maxAllowed = 255;
! 
!     while(1) {
!         /* twice so we have enough tries to find guys we GC after one pass;
!          * 10 extra is just in case I mis-counted.
!          */
!         if (++counter > 2*maxAllowed+10) osi_panic("afsd: dir search cookie leak",
!                                                     __FILE__, __LINE__);
!         if (smb_dirSearchCounter > maxAllowed) {	
!             smb_dirSearchCounter = 1;
!             smb_GCDirSearches(isV3);	/* GC some (drops global lock) */
!         }	
!         dsp = smb_FindDirSearchNL(smb_dirSearchCounter);
!         if (dsp) {
!             /* don't need to watch for refcount zero and deleted, since
!             * we haven't dropped the global lock.
!             */
!             dsp->refCount--;
!             ++smb_dirSearchCounter;
!             continue;
!         }	
! 
!         dsp = malloc(sizeof(*dsp));
!         memset(dsp, 0, sizeof(*dsp));
!         osi_QAdd((osi_queue_t **) &smb_firstDirSearchp, &dsp->q);
!         if (!smb_lastDirSearchp) smb_lastDirSearchp = (smb_dirSearch_t *) &dsp->q;
!         dsp->cookie = smb_dirSearchCounter;
!         ++smb_dirSearchCounter;
!         dsp->refCount = 1;
!         lock_InitializeMutex(&dsp->mx, "cm_dirSearch_t");
!         dsp->lastTime = osi_Time();
!         break;
!     }	
!     lock_ReleaseWrite(&smb_globalLock);
!     return dsp;
  }
  
  static smb_packet_t *GetPacket(void)
  {
!     smb_packet_t *tbp;
  #ifdef DJGPP
!     unsigned int npar, seg, tb_sel;
  #endif
  
!     lock_ObtainWrite(&smb_globalLock);
!     tbp = smb_packetFreeListp;
      if (tbp) 
          smb_packetFreeListp = tbp->nextp;
!     lock_ReleaseWrite(&smb_globalLock);
      if (!tbp) {
  #ifndef DJGPP
          tbp = calloc(65540,1);
***************
*** 1721,1736 ****
          tbp = malloc(sizeof(smb_packet_t));
  #endif /* !DJGPP */
          tbp->magic = SMB_PACKETMAGIC;
! 		tbp->ncbp = NULL;
! 		tbp->vcp = NULL;
! 		tbp->resumeCode = 0;
! 		tbp->inCount = 0;
! 		tbp->fid = 0;
! 		tbp->wctp = NULL;
! 		tbp->inCom = 0;
! 		tbp->oddByte = 0;
! 		tbp->ncb_length = 0;
! 		tbp->flags = 0;
          tbp->spacep = NULL;
          
  #ifdef DJGPP
--- 1734,1749 ----
          tbp = malloc(sizeof(smb_packet_t));
  #endif /* !DJGPP */
          tbp->magic = SMB_PACKETMAGIC;
!         tbp->ncbp = NULL;
!         tbp->vcp = NULL;
!         tbp->resumeCode = 0;
!         tbp->inCount = 0;
!         tbp->fid = 0;
!         tbp->wctp = NULL;
!         tbp->inCom = 0;
!         tbp->oddByte = 0;
!         tbp->ncb_length = 0;
!         tbp->flags = 0;
          tbp->spacep = NULL;
          
  #ifdef DJGPP
***************
*** 1752,1758 ****
          tbp->dos_pkt = (seg * 16) + 0;  /* DOS physical address */
          tbp->dos_pkt_sel = tb_sel;
  #endif /* DJGPP */
! 	}
      osi_assert(tbp->magic == SMB_PACKETMAGIC);
  
      return tbp;
--- 1765,1771 ----
          tbp->dos_pkt = (seg * 16) + 0;  /* DOS physical address */
          tbp->dos_pkt_sel = tb_sel;
  #endif /* DJGPP */
!     }
      osi_assert(tbp->magic == SMB_PACKETMAGIC);
  
      return tbp;
***************
*** 1760,1785 ****
  
  smb_packet_t *smb_CopyPacket(smb_packet_t *pkt)
  {
! 	smb_packet_t *tbp;
! 	tbp = GetPacket();
! 	memcpy(tbp, pkt, sizeof(smb_packet_t));
! 	tbp->wctp = tbp->data + ((unsigned int)pkt->wctp - (unsigned int)pkt->data);
! 	return tbp;
  }
  
  static NCB *GetNCB(void)
  {
! 	smb_ncb_t *tbp;
      NCB *ncbp;
  #ifdef DJGPP
      unsigned int npar, seg, tb_sel;
  #endif /* DJGPP */
  
! 	lock_ObtainWrite(&smb_globalLock);
! 	tbp = smb_ncbFreeListp;
      if (tbp) 
          smb_ncbFreeListp = tbp->nextp;
! 	lock_ReleaseWrite(&smb_globalLock);
      if (!tbp) {
  #ifndef DJGPP
          tbp = calloc(sizeof(*tbp),1);
--- 1773,1798 ----
  
  smb_packet_t *smb_CopyPacket(smb_packet_t *pkt)
  {
!     smb_packet_t *tbp;
!     tbp = GetPacket();
!     memcpy(tbp, pkt, sizeof(smb_packet_t));
!     tbp->wctp = tbp->data + ((unsigned int)pkt->wctp - (unsigned int)pkt->data);
!     return tbp;
  }
  
  static NCB *GetNCB(void)
  {
!     smb_ncb_t *tbp;
      NCB *ncbp;
  #ifdef DJGPP
      unsigned int npar, seg, tb_sel;
  #endif /* DJGPP */
  
!     lock_ObtainWrite(&smb_globalLock);
!     tbp = smb_ncbFreeListp;
      if (tbp) 
          smb_ncbFreeListp = tbp->nextp;
!     lock_ReleaseWrite(&smb_globalLock);
      if (!tbp) {
  #ifndef DJGPP
          tbp = calloc(sizeof(*tbp),1);
***************
*** 1803,1813 ****
          tbp->dos_ncb_sel = tb_sel;
  #endif /* !DJGPP */
          tbp->magic = SMB_NCBMAGIC;
! 	}
          
      osi_assert(tbp->magic == SMB_NCBMAGIC);
  
! 	memset(&tbp->ncb, 0, sizeof(NCB));
      ncbp = &tbp->ncb;
  #ifdef DJGPP
      dos_memset(tbp->dos_ncb, 0, sizeof(NCB));
--- 1816,1826 ----
          tbp->dos_ncb_sel = tb_sel;
  #endif /* !DJGPP */
          tbp->magic = SMB_NCBMAGIC;
!     }
          
      osi_assert(tbp->magic == SMB_NCBMAGIC);
  
!     memset(&tbp->ncb, 0, sizeof(NCB));
      ncbp = &tbp->ncb;
  #ifdef DJGPP
      dos_memset(tbp->dos_ncb, 0, sizeof(NCB));
***************
*** 1820,1851 ****
      osi_assert(tbp->magic == SMB_PACKETMAGIC);
          
      lock_ObtainWrite(&smb_globalLock);
! 	tbp->nextp = smb_packetFreeListp;
! 	smb_packetFreeListp = tbp;
! 	tbp->magic = SMB_PACKETMAGIC;
! 	tbp->ncbp = NULL;
! 	tbp->vcp = NULL;
! 	tbp->resumeCode = 0;
! 	tbp->inCount = 0;
! 	tbp->fid = 0;
! 	tbp->wctp = NULL;
! 	tbp->inCom = 0;
! 	tbp->oddByte = 0;
! 	tbp->ncb_length = 0;
! 	tbp->flags = 0;
      lock_ReleaseWrite(&smb_globalLock);
  }
  
  static void FreeNCB(NCB *bufferp)
  {
! 	smb_ncb_t *tbp;
          
      tbp = (smb_ncb_t *) bufferp;
      osi_assert(tbp->magic == SMB_NCBMAGIC);
          
      lock_ObtainWrite(&smb_globalLock);
! 	tbp->nextp = smb_ncbFreeListp;
! 	smb_ncbFreeListp = tbp;
      lock_ReleaseWrite(&smb_globalLock);
  }
  
--- 1833,1864 ----
      osi_assert(tbp->magic == SMB_PACKETMAGIC);
          
      lock_ObtainWrite(&smb_globalLock);
!     tbp->nextp = smb_packetFreeListp;
!     smb_packetFreeListp = tbp;
!     tbp->magic = SMB_PACKETMAGIC;
!     tbp->ncbp = NULL;
!     tbp->vcp = NULL;
!     tbp->resumeCode = 0;
!     tbp->inCount = 0;
!     tbp->fid = 0;
!     tbp->wctp = NULL;
!     tbp->inCom = 0;
!     tbp->oddByte = 0;
!     tbp->ncb_length = 0;
!     tbp->flags = 0;
      lock_ReleaseWrite(&smb_globalLock);
  }
  
  static void FreeNCB(NCB *bufferp)
  {
!     smb_ncb_t *tbp;
          
      tbp = (smb_ncb_t *) bufferp;
      osi_assert(tbp->magic == SMB_NCBMAGIC);
          
      lock_ObtainWrite(&smb_globalLock);
!     tbp->nextp = smb_ncbFreeListp;
!     smb_ncbFreeListp = tbp;
      lock_ReleaseWrite(&smb_globalLock);
  }
  
***************
*** 1857,1868 ****
      unsigned char *afterParmsp;
  
      parmBytes = *smbp->wctp << 1;
! 	afterParmsp = smbp->wctp + parmBytes + 1;
          
      dataBytes = afterParmsp[0] + (afterParmsp[1]<<8);
      if (nbytesp) *nbytesp = dataBytes;
          
! 	/* don't forget to skip the data byte count, since it follows
       * the parameters; that's where the "2" comes from below.
       */
      return (unsigned char *) (afterParmsp + 2);
--- 1870,1881 ----
      unsigned char *afterParmsp;
  
      parmBytes = *smbp->wctp << 1;
!     afterParmsp = smbp->wctp + parmBytes + 1;
          
      dataBytes = afterParmsp[0] + (afterParmsp[1]<<8);
      if (nbytesp) *nbytesp = dataBytes;
          
!     /* don't forget to skip the data byte count, since it follows
       * the parameters; that's where the "2" comes from below.
       */
      return (unsigned char *) (afterParmsp + 2);
***************
*** 1874,2454 ****
   */
  void smb_SetSMBDataLength(smb_packet_t *smbp, unsigned int dsize)
  {
! 	unsigned char *afterParmsp;
  
! 	afterParmsp = smbp->wctp + ((*smbp->wctp)<<1) + 1;
          
! 	*afterParmsp++ = dsize & 0xff;
! 	*afterParmsp = (dsize>>8) & 0xff;
! }
  
  /* return the parm'th parameter in the smbp packet */
  unsigned int smb_GetSMBParm(smb_packet_t *smbp, int parm)
  {
! 	int parmCount;
! 	unsigned char *parmDatap;
  
! 	parmCount = *smbp->wctp;
  
! 	if (parm >= parmCount) {
! 		char s[100];
  #ifndef DJGPP
          HANDLE h;
! 		char *ptbuf[1];
! 		h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
! #endif
! 		sprintf(s, "Bad SMB param %d out of %d, ncb len %d",
! 				parm, parmCount, smbp->ncb_length);
! #ifndef DJGPP
! 		ptbuf[0] = s;
! 		ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, 1006, NULL,
! 					1, smbp->ncb_length, ptbuf, smbp);
! 		DeregisterEventSource(h);
  #endif
          osi_Log0(smb_logp, osi_LogSaveString(smb_logp, s));
! 		osi_panic(s, __FILE__, __LINE__);
! 	}
! 	parmDatap = smbp->wctp + (2*parm) + 1;
          
! 	return parmDatap[0] + (parmDatap[1] << 8);
  }
  
  /* return the parm'th parameter in the smbp packet */
  unsigned int smb_GetSMBOffsetParm(smb_packet_t *smbp, int parm, int offset)
  {
! 	int parmCount;
! 	unsigned char *parmDatap;
  
! 	parmCount = *smbp->wctp;
  
! 	if (parm * 2 + offset >= parmCount * 2) {
! 		char s[100];
  #ifndef DJGPP
! 		HANDLE h;
! 		char *ptbuf[1];
! 		h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
  #endif
! 		sprintf(s, "Bad SMB param %d offset %d out of %d, ncb len %d",
! 				parm, offset, parmCount, smbp->ncb_length);
  #ifndef DJGPP
          ptbuf[0] = s;
! 		ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, 1006, NULL,
! 					1, smbp->ncb_length, ptbuf, smbp);
! 		DeregisterEventSource(h);
  #endif
          osi_Log0(smb_logp, osi_LogSaveString(smb_logp, s));
! 		osi_panic(s, __FILE__, __LINE__);
! 	}
! 	parmDatap = smbp->wctp + (2*parm) + 1 + offset;
  	
! 	return parmDatap[0] + (parmDatap[1] << 8);
  }
  
  void smb_SetSMBParm(smb_packet_t *smbp, int slot, unsigned int parmValue)
  {
! 	char *parmDatap;
  
! 	/* make sure we have enough slots */
! 	if (*smbp->wctp <= slot) *smbp->wctp = slot+1;
!         
! 	parmDatap = smbp->wctp + 2*slot + 1 + smbp->oddByte;
! 	*parmDatap++ = parmValue & 0xff;
! 	*parmDatap = (parmValue>>8) & 0xff;
! }
  
  void smb_SetSMBParmLong(smb_packet_t *smbp, int slot, unsigned int parmValue)
  {
! 	char *parmDatap;
! 
! 	/* make sure we have enough slots */
! 	if (*smbp->wctp <= slot) *smbp->wctp = slot+2;
  
! 	parmDatap = smbp->wctp + 2*slot + 1 + smbp->oddByte;
! 	*parmDatap++ = parmValue & 0xff;
! 	*parmDatap++ = (parmValue>>8) & 0xff;
! 	*parmDatap++ = (parmValue>>16) & 0xff;
! 	*parmDatap++ = (parmValue>>24) & 0xff;
  }
  
  void smb_SetSMBParmDouble(smb_packet_t *smbp, int slot, char *parmValuep)
  {
! 	char *parmDatap;
! 	int i;
! 
! 	/* make sure we have enough slots */
! 	if (*smbp->wctp <= slot) *smbp->wctp = slot+4;
  
! 	parmDatap = smbp->wctp + 2*slot + 1 + smbp->oddByte;
! 	for (i=0; i<8; i++)
! 		*parmDatap++ = *parmValuep++;
! }
  
  void smb_SetSMBParmByte(smb_packet_t *smbp, int slot, unsigned int parmValue)
  {
! 	char *parmDatap;
  
! 	/* make sure we have enough slots */
! 	if (*smbp->wctp <= slot) {
! 		if (smbp->oddByte) {
! 			smbp->oddByte = 0;
! 			*smbp->wctp = slot+1;
! 		} else
! 			smbp->oddByte = 1;
! 	}
  
! 	parmDatap = smbp->wctp + 2*slot + 1 + (1 - smbp->oddByte);
! 	*parmDatap++ = parmValue & 0xff;
  }
  
  void smb_StripLastComponent(char *outPathp, char **lastComponentp, char *inPathp)
  {
! 	char *lastSlashp;
          
! 	lastSlashp = strrchr(inPathp, '\\');
! 	if (lastComponentp)
! 		*lastComponentp = lastSlashp;
! 	if (lastSlashp) {
! 		while (1) {
! 			if (inPathp == lastSlashp) 
! 				break;
! 			*outPathp++ = *inPathp++;
! 		}
! 		*outPathp++ = 0;
! 	}
! 	else {
! 		*outPathp++ = 0;
! 	}
  }
  
  unsigned char *smb_ParseASCIIBlock(unsigned char *inp, char **chainpp)
  {
! 	if (*inp++ != 0x4) 
! 		return NULL;
! 	if (chainpp) {
! 		*chainpp = inp + strlen(inp) + 1;	/* skip over null-terminated string */
! 	}
! 	return inp;
  }
  
  unsigned char *smb_ParseVblBlock(unsigned char *inp, char **chainpp, int *lengthp)
  {
! 	int tlen;
  
! 	if (*inp++ != 0x5) 
! 		return NULL;
! 	tlen = inp[0] + (inp[1]<<8);
! 	inp += 2;		/* skip length field */
!         
! 	if (chainpp) {
! 		*chainpp = inp + tlen;
! 	}
          
! 	if (lengthp) 
! 		*lengthp = tlen;
          
! 	return inp;
  }	
  
  /* format a packet as a response */
  void smb_FormatResponsePacket(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *op)
  {
! 	smb_t *outp;
! 	smb_t *inSmbp;
  
! 	outp = (smb_t *) op;
  	
! 	/* zero the basic structure through the smb_wct field, and zero the data
! 	 * size field, assuming that wct stays zero; otherwise, you have to 
! 	 * explicitly set the data size field, too.
! 	 */
! 	inSmbp = (smb_t *) inp;
! 	memset(outp, 0, sizeof(smb_t)+2);
! 	outp->id[0] = 0xff;
! 	outp->id[1] = 'S';
! 	outp->id[2] = 'M';
! 	outp->id[3] = 'B';
! 	if (inp) {
! 		outp->com = inSmbp->com;
! 		outp->tid = inSmbp->tid;
! 		outp->pid = inSmbp->pid;
! 		outp->uid = inSmbp->uid;
! 		outp->mid = inSmbp->mid;
! 		outp->res[0] = inSmbp->res[0];
! 		outp->res[1] = inSmbp->res[1];
! 		op->inCom = inSmbp->com;
! 	}
! 	outp->reb = 0x80;	/* SERVER_RESP */
! 	outp->flg2 = 0x1;	/* KNOWS_LONG_NAMES */
! 
! 	/* copy fields in generic packet area */
! 	op->wctp = &outp->wct;
! }
  
  /* send a (probably response) packet; vcp tells us to whom to send it.
   * we compute the length by looking at wct and bcc fields.
   */
  void smb_SendPacket(smb_vc_t *vcp, smb_packet_t *inp)
  {
! 	NCB *ncbp;
! 	int extra;
! 	long code = 0;
! 	unsigned char *tp;
! 	int localNCB = 0;
  #ifdef DJGPP
! 	dos_ptr dos_ncb;
  #endif /* DJGPP */
          
! 	ncbp = inp->ncbp;
! 	if (ncbp == NULL) {
! 		ncbp = GetNCB();
! 		localNCB = 1;
! 	}
  #ifdef DJGPP
! 	dos_ncb = ((smb_ncb_t *)ncbp)->dos_ncb;
  #endif /* DJGPP */
   
! 	memset((char *)ncbp, 0, sizeof(NCB));
  
! 	extra = 2 * (*inp->wctp);	/* space used by parms, in bytes */
! 	tp = inp->wctp + 1+ extra;	/* points to count of data bytes */
! 	extra += tp[0] + (tp[1]<<8);
! 	extra += ((unsigned int)inp->wctp - (unsigned int)inp->data);	/* distance to last wct field */
! 	extra += 3;			/* wct and length fields */
!         
! 	ncbp->ncb_length = extra;	/* bytes to send */
! 	ncbp->ncb_lsn = (unsigned char) vcp->lsn;	/* vc to use */
! 	ncbp->ncb_lana_num = vcp->lana;
! 	ncbp->ncb_command = NCBSEND;	/* op means send data */
  #ifndef DJGPP
! 	ncbp->ncb_buffer = (char *) inp;/* packet */
! 	code = Netbios(ncbp);
  #else /* DJGPP */
! 	ncbp->ncb_buffer = inp->dos_pkt;/* packet */
! 	((smb_ncb_t*)ncbp)->orig_pkt = inp;
  
! 	/* copy header information from virtual to DOS address space */
! 	dosmemput((char*)inp, SMB_PACKETSIZE, inp->dos_pkt);
! 	code = Netbios(ncbp, dos_ncb);
  #endif /* !DJGPP */
          
! 	if (code != 0)
! 		osi_Log1(smb_logp, "SendPacket failure code %d", code);
  
! 	if (localNCB)
! 		FreeNCB(ncbp);
  }
  
  void smb_MapNTError(long code, unsigned long *NTStatusp)
  {
! 	unsigned long NTStatus;
  
! 	/* map CM_ERROR_* errors to NT 32-bit status codes */
      /* NT Status codes are listed in ntstatus.h not winerror.h */
! 	if (code == CM_ERROR_NOSUCHCELL) {
! 		NTStatus = 0xC000000FL;	/* No such file */
! 	}
! 	else if (code == CM_ERROR_NOSUCHVOLUME) {
! 		NTStatus = 0xC000000FL;	/* No such file */
! 	}
! 	else if (code == CM_ERROR_TIMEDOUT) {
! 		NTStatus = 0xC00000CFL;	/* Sharing Paused */
! 	}
! 	else if (code == CM_ERROR_RETRY) {
! 		NTStatus = 0xC000022DL;	/* Retry */
! 	}
! 	else if (code == CM_ERROR_NOACCESS) {
! 		NTStatus = 0xC0000022L;	/* Access denied */
! 	}
! 	else if (code == CM_ERROR_READONLY) {
! 		NTStatus = 0xC00000A2L;	/* Write protected */
! 	}	
! 	else if (code == CM_ERROR_NOSUCHFILE) {
! 		NTStatus = 0xC000000FL;	/* No such file */
! 	}
! 	else if (code == CM_ERROR_NOSUCHPATH) {
! 		NTStatus = 0xC000003AL;	/* Object path not found */
! 	}		
! 	else if (code == CM_ERROR_TOOBIG) {
! 		NTStatus = 0xC000007BL;	/* Invalid image format */
! 	}
! 	else if (code == CM_ERROR_INVAL) {
! 		NTStatus = 0xC000000DL;	/* Invalid parameter */
! 	}
! 	else if (code == CM_ERROR_BADFD) {
! 		NTStatus = 0xC0000008L;	/* Invalid handle */
! 	}
! 	else if (code == CM_ERROR_BADFDOP) {
! 		NTStatus = 0xC0000022L;	/* Access denied */
! 	}
! 	else if (code == CM_ERROR_EXISTS) {
! 		NTStatus = 0xC0000035L;	/* Object name collision */
! 	}
! 	else if (code == CM_ERROR_NOTEMPTY) {
! 		NTStatus = 0xC0000101L;	/* Directory not empty */
! 	}	
! 	else if (code == CM_ERROR_CROSSDEVLINK) {
! 		NTStatus = 0xC00000D4L;	/* Not same device */
! 	}
! 	else if (code == CM_ERROR_NOTDIR) {
! 		NTStatus = 0xC0000103L;	/* Not a directory */
! 	}
! 	else if (code == CM_ERROR_ISDIR) {
! 		NTStatus = 0xC00000BAL;	/* File is a directory */
! 	}
! 	else if (code == CM_ERROR_BADOP) {
! 		NTStatus = 0xC09820FFL;	/* SMB no support */
! 	}
! 	else if (code == CM_ERROR_BADSHARENAME) {
! 		NTStatus = 0xC00000CCL;	/* Bad network name */
! 	}
! 	else if (code == CM_ERROR_NOIPC) {
  #ifdef COMMENT
! 		NTStatus = 0xC0000022L;	/* Access Denied */
  #else
!         NTStatus = 0xC000013DL; /* Remote Resources */
  #endif
! 	}
! 	else if (code == CM_ERROR_CLOCKSKEW) {
! 		NTStatus = 0xC0000133L;	/* Time difference at DC */
! 	}
! 	else if (code == CM_ERROR_BADTID) {
! 		NTStatus = 0xC0982005L;	/* SMB bad TID */
! 	}
! 	else if (code == CM_ERROR_USESTD) {
! 		NTStatus = 0xC09820FBL;	/* SMB use standard */
! 	}
! 	else if (code == CM_ERROR_QUOTA) {
! 		NTStatus = 0xC0000044L;	/* Quota exceeded */
! 	}
! 	else if (code == CM_ERROR_SPACE) {
! 		NTStatus = 0xC000007FL;	/* Disk full */
! 	}
! 	else if (code == CM_ERROR_ATSYS) {
! 		NTStatus = 0xC0000033L;	/* Object name invalid */
! 	}
! 	else if (code == CM_ERROR_BADNTFILENAME) {
! 		NTStatus = 0xC0000033L;	/* Object name invalid */
! 	}
! 	else if (code == CM_ERROR_WOULDBLOCK) {
! 		NTStatus = 0xC0000055L;	/* Lock not granted */
! 	}
! 	else if (code == CM_ERROR_PARTIALWRITE) {
! 		NTStatus = 0xC000007FL;	/* Disk full */
! 	}
! 	else if (code == CM_ERROR_BUFFERTOOSMALL) {
! 		NTStatus = 0xC0000023L;	/* Buffer too small */
! 	}
      else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
! 		NTStatus = 0xC0000035L;	/* Object name collision */
      }
- 	else if (code == CM_ERROR_BADPASSWORD) {
- 		NTStatus = 0xC000006DL; /* unknown username or bad password */
- 	}
- 	else if (code == CM_ERROR_BADLOGONTYPE) {
- 		NTStatus = 0xC000015BL; /* logon type not granted */
- 	}
- 	else if (code == CM_ERROR_GSSCONTINUE) {
- 		NTStatus = 0xC0000016L; /* more processing required */
- 	}
- 	else {
- 		NTStatus = 0xC0982001L;	/* SMB non-specific error */
- 	}
  
! 	*NTStatusp = NTStatus;
! 	osi_Log2(smb_logp, "SMB SEND code %lX as NT %lX", code, NTStatus);
! }
  
  void smb_MapCoreError(long code, smb_vc_t *vcp, unsigned short *scodep,
! 	unsigned char *classp)
  {
! 	unsigned char class;
! 	unsigned short error;
  
! 	/* map CM_ERROR_* errors to SMB errors */
! 	if (code == CM_ERROR_NOSUCHCELL) {
! 		class = 1;
! 		error = 3;	/* bad path */
! 	}
! 	else if (code == CM_ERROR_NOSUCHVOLUME) {
! 		class = 1;
! 		error = 3;	/* bad path */
! 	}
! 	else if (code == CM_ERROR_TIMEDOUT) {
! 		class = 2;
! 		error = 81;	/* server is paused */
! 	}
! 	else if (code == CM_ERROR_RETRY) {
! 		class = 2;	/* shouldn't happen */
! 		error = 1;
! 	}
! 	else if (code == CM_ERROR_NOACCESS) {
! 		class = 2;
! 		error = 4;	/* bad access */
! 	}
! 	else if (code == CM_ERROR_READONLY) {
! 		class = 3;
! 		error = 19;	/* read only */
! 	}
! 	else if (code == CM_ERROR_NOSUCHFILE) {
! 		class = 1;
! 		error = 2;	/* ENOENT! */
! 	}
! 	else if (code == CM_ERROR_NOSUCHPATH) {
! 		class = 1;
! 		error = 3;	/* Bad path */
! 	}
! 	else if (code == CM_ERROR_TOOBIG) {
! 		class = 1;
! 		error = 11;	/* bad format */
! 	}
! 	else if (code == CM_ERROR_INVAL) {
! 		class = 2;	/* server non-specific error code */
! 		error = 1;
! 	}
! 	else if (code == CM_ERROR_BADFD) {
! 		class = 1;
! 		error = 6;	/* invalid file handle */
! 	}
! 	else if (code == CM_ERROR_BADFDOP) {
! 		class = 1;	/* invalid op on FD */
! 		error = 5;
! 	}
! 	else if (code == CM_ERROR_EXISTS) {
! 		class = 1;
! 		error = 80;	/* file already exists */
! 	}
! 	else if (code == CM_ERROR_NOTEMPTY) {
! 		class = 1;
! 		error = 5;	/* delete directory not empty */
! 	}
! 	else if (code == CM_ERROR_CROSSDEVLINK) {
! 		class = 1;
! 		error = 17;	/* EXDEV */
! 	}
! 	else if (code == CM_ERROR_NOTDIR) {
! 		class = 1;	/* bad path */
! 		error = 3;
! 	}
! 	else if (code == CM_ERROR_ISDIR) {
! 		class = 1;	/* access denied; DOS doesn't have a good match */
! 		error = 5;
! 	}
! 	else if (code == CM_ERROR_BADOP) {
! 		class = 2;
! 		error = 65535;
! 	}
! 	else if (code == CM_ERROR_BADSHARENAME) {
! 		class = 2;
! 		error = 6;
! 	}
! 	else if (code == CM_ERROR_NOIPC) {
! 		class = 2;
! 		error = 4; /* bad access */
! 	}
! 	else if (code == CM_ERROR_CLOCKSKEW) {
! 		class = 1;	/* invalid function */
! 		error = 1;
! 	}
! 	else if (code == CM_ERROR_BADTID) {
! 		class = 2;
! 		error = 5;
! 	}
! 	else if (code == CM_ERROR_USESTD) {
! 		class = 2;
! 		error = 251;
! 	}
! 	else if (code == CM_ERROR_REMOTECONN) {
! 		class = 2;
! 		error = 82;
! 	}
! 	else if (code == CM_ERROR_QUOTA) {
! 		if (vcp->flags & SMB_VCFLAG_USEV3) {
! 			class = 3;
! 			error = 39;	/* disk full */
! 		}
! 		else {
! 			class = 1;
! 			error = 5;	/* access denied */
! 		}
! 	}
! 	else if (code == CM_ERROR_SPACE) {
! 		if (vcp->flags & SMB_VCFLAG_USEV3) {
! 			class = 3;
! 			error = 39;	/* disk full */
! 		}
! 		else {
! 			class = 1;
! 			error = 5;	/* access denied */
! 		}
! 	}
! 	else if (code == CM_ERROR_PARTIALWRITE) {
! 		class = 3;
! 		error = 39;	/* disk full */
! 	}
! 	else if (code == CM_ERROR_ATSYS) {
! 		class = 1;
! 		error = 2;	/* ENOENT */
! 	}
! 	else if (code == CM_ERROR_WOULDBLOCK) {
! 		class = 1;
! 		error = 33;	/* lock conflict */
! 	}
! 	else if (code == CM_ERROR_NOFILES) {
! 		class = 1;
! 		error = 18;	/* no files in search */
! 	}
! 	else if (code == CM_ERROR_RENAME_IDENTICAL) {
! 		class = 1;
! 		error = 183;     /* Samba uses this */
! 	}
! 	else if (code == CM_ERROR_BADPASSWORD || code == CM_ERROR_BADLOGONTYPE) {
! 		/* we don't have a good way of reporting CM_ERROR_BADLOGONTYPE */
! 		class = 2;
! 		error = 2; /* bad password */
! 	}
! 	else {
! 		class = 2;
! 		error = 1;
! 	}
! 
! 	*scodep = error;
! 	*classp = class;
! 	osi_Log3(smb_logp, "SMB SEND code %lX as SMB %d: %d", code, class, error);
! }
  
  long smb_SendCoreBadOp(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
! 	return CM_ERROR_BADOP;
  }
  
  long smb_ReceiveCoreEcho(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
! 	unsigned short EchoCount, i;
! 	char *data, *outdata;
! 	int dataSize;
! 
! 	EchoCount = (unsigned short) smb_GetSMBParm(inp, 0);
! 
! 	for (i=1; i<=EchoCount; i++) {
! 	    data = smb_GetSMBData(inp, &dataSize);
! 	    smb_SetSMBParm(outp, 0, i);
! 	    smb_SetSMBDataLength(outp, dataSize);
!             outdata = smb_GetSMBData(outp, NULL);
! 	    memcpy(outdata, data, dataSize);
! 	    smb_SendPacket(vcp, outp);
! 	}
  
! 	return 0;
  }
  
  long smb_ReceiveCoreReadRaw(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
! 	osi_hyper_t offset;
! 	long count, minCount, finalCount;
! 	unsigned short fd;
! 	smb_fid_t *fidp;
! 	long code = 0;
! 	cm_user_t *userp = NULL;
      NCB *ncbp;
      int rc;
  #ifndef DJGPP
--- 1887,2480 ----
   */
  void smb_SetSMBDataLength(smb_packet_t *smbp, unsigned int dsize)
  {
!     unsigned char *afterParmsp;
  
!     afterParmsp = smbp->wctp + ((*smbp->wctp)<<1) + 1;
          
!     *afterParmsp++ = dsize & 0xff;
!     *afterParmsp = (dsize>>8) & 0xff;
! }       
  
  /* return the parm'th parameter in the smbp packet */
  unsigned int smb_GetSMBParm(smb_packet_t *smbp, int parm)
  {
!     int parmCount;
!     unsigned char *parmDatap;
  
!     parmCount = *smbp->wctp;
  
!     if (parm >= parmCount) {
!         char s[100];
  #ifndef DJGPP
          HANDLE h;
!         char *ptbuf[1];
!         h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
! #endif  
!         sprintf(s, "Bad SMB param %d out of %d, ncb len %d",
!                  parm, parmCount, smbp->ncb_length);
! #ifndef DJGPP   
!         ptbuf[0] = s;
!         ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, 1006, NULL,
!                      1, smbp->ncb_length, ptbuf, smbp);
!         DeregisterEventSource(h);
  #endif
          osi_Log0(smb_logp, osi_LogSaveString(smb_logp, s));
!         osi_panic(s, __FILE__, __LINE__);
!     }
!     parmDatap = smbp->wctp + (2*parm) + 1;
          
!     return parmDatap[0] + (parmDatap[1] << 8);
  }
  
  /* return the parm'th parameter in the smbp packet */
  unsigned int smb_GetSMBOffsetParm(smb_packet_t *smbp, int parm, int offset)
  {
!     int parmCount;
!     unsigned char *parmDatap;
  
!     parmCount = *smbp->wctp;
  
!     if (parm * 2 + offset >= parmCount * 2) {
!         char s[100];
  #ifndef DJGPP
!         HANDLE h;
!         char *ptbuf[1];
!         h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
  #endif
!         sprintf(s, "Bad SMB param %d offset %d out of %d, ncb len %d",
!                 parm, offset, parmCount, smbp->ncb_length);
  #ifndef DJGPP
          ptbuf[0] = s;
!         ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, 1006, NULL,
!                     1, smbp->ncb_length, ptbuf, smbp);
!         DeregisterEventSource(h);
  #endif
          osi_Log0(smb_logp, osi_LogSaveString(smb_logp, s));
!         osi_panic(s, __FILE__, __LINE__);
!     }
!     parmDatap = smbp->wctp + (2*parm) + 1 + offset;
  	
!     return parmDatap[0] + (parmDatap[1] << 8);
  }
  
  void smb_SetSMBParm(smb_packet_t *smbp, int slot, unsigned int parmValue)
  {
!     char *parmDatap;
  
!     /* make sure we have enough slots */
!     if (*smbp->wctp <= slot) 
!         *smbp->wctp = slot+1;
!         
!     parmDatap = smbp->wctp + 2*slot + 1 + smbp->oddByte;
!     *parmDatap++ = parmValue & 0xff;
!     *parmDatap = (parmValue>>8) & 0xff;
! }       
  
  void smb_SetSMBParmLong(smb_packet_t *smbp, int slot, unsigned int parmValue)
  {
!     char *parmDatap;
  
!     /* make sure we have enough slots */
!     if (*smbp->wctp <= slot) 
!         *smbp->wctp = slot+2;
! 
!     parmDatap = smbp->wctp + 2*slot + 1 + smbp->oddByte;
!     *parmDatap++ = parmValue & 0xff;
!     *parmDatap++ = (parmValue>>8) & 0xff;
!     *parmDatap++ = (parmValue>>16) & 0xff;
!     *parmDatap++ = (parmValue>>24) & 0xff;
  }
  
  void smb_SetSMBParmDouble(smb_packet_t *smbp, int slot, char *parmValuep)
  {
!     char *parmDatap;
!     int i;
  
!     /* make sure we have enough slots */
!     if (*smbp->wctp <= slot) 
!         *smbp->wctp = slot+4;
! 
!     parmDatap = smbp->wctp + 2*slot + 1 + smbp->oddByte;
!     for (i=0; i<8; i++)
!         *parmDatap++ = *parmValuep++;
! }       
  
  void smb_SetSMBParmByte(smb_packet_t *smbp, int slot, unsigned int parmValue)
  {
!     char *parmDatap;
  
!     /* make sure we have enough slots */
!     if (*smbp->wctp <= slot) {
!         if (smbp->oddByte) {
!             smbp->oddByte = 0;
!             *smbp->wctp = slot+1;
!         } else
!             smbp->oddByte = 1;
!     }
  
!     parmDatap = smbp->wctp + 2*slot + 1 + (1 - smbp->oddByte);
!     *parmDatap++ = parmValue & 0xff;
  }
  
  void smb_StripLastComponent(char *outPathp, char **lastComponentp, char *inPathp)
  {
!     char *lastSlashp;
          
!     lastSlashp = strrchr(inPathp, '\\');
!     if (lastComponentp)
!         *lastComponentp = lastSlashp;
!     if (lastSlashp) {
!         while (1) {
!             if (inPathp == lastSlashp) 
!                 break;
!             *outPathp++ = *inPathp++;
!         }
!         *outPathp++ = 0;
!     }
!     else {
!         *outPathp++ = 0;
!     }
  }
  
  unsigned char *smb_ParseASCIIBlock(unsigned char *inp, char **chainpp)
  {
!     if (*inp++ != 0x4) 
!         return NULL;
!     if (chainpp) {
!         *chainpp = inp + strlen(inp) + 1;	/* skip over null-terminated string */
!     }
!     return inp;
  }
  
  unsigned char *smb_ParseVblBlock(unsigned char *inp, char **chainpp, int *lengthp)
  {
!     int tlen;
  
!     if (*inp++ != 0x5) 
!         return NULL;
!     tlen = inp[0] + (inp[1]<<8);
!     inp += 2;		/* skip length field */
! 
!     if (chainpp) {
!         *chainpp = inp + tlen;
!     }
          
!     if (lengthp) 
!         *lengthp = tlen;
          
!     return inp;
  }	
  
  /* format a packet as a response */
  void smb_FormatResponsePacket(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *op)
  {
!     smb_t *outp;
!     smb_t *inSmbp;
  
!     outp = (smb_t *) op;
  	
!     /* zero the basic structure through the smb_wct field, and zero the data
!      * size field, assuming that wct stays zero; otherwise, you have to 
!      * explicitly set the data size field, too.
!      */
!     inSmbp = (smb_t *) inp;
!     memset(outp, 0, sizeof(smb_t)+2);
!     outp->id[0] = 0xff;
!     outp->id[1] = 'S';
!     outp->id[2] = 'M';
!     outp->id[3] = 'B';
!     if (inp) {
!         outp->com = inSmbp->com;
!         outp->tid = inSmbp->tid;
!         outp->pid = inSmbp->pid;
!         outp->uid = inSmbp->uid;
!         outp->mid = inSmbp->mid;
!         outp->res[0] = inSmbp->res[0];
!         outp->res[1] = inSmbp->res[1];
!         op->inCom = inSmbp->com;
!     }
!     outp->reb = 0x80;	/* SERVER_RESP */
!     outp->flg2 = 0x1;	/* KNOWS_LONG_NAMES */
! 
!     /* copy fields in generic packet area */
!     op->wctp = &outp->wct;
! }       
  
  /* send a (probably response) packet; vcp tells us to whom to send it.
   * we compute the length by looking at wct and bcc fields.
   */
  void smb_SendPacket(smb_vc_t *vcp, smb_packet_t *inp)
  {
!     NCB *ncbp;
!     int extra;
!     long code = 0;
!     unsigned char *tp;
!     int localNCB = 0;
  #ifdef DJGPP
!     dos_ptr dos_ncb;
  #endif /* DJGPP */
          
!     ncbp = inp->ncbp;
!     if (ncbp == NULL) {
!         ncbp = GetNCB();
!         localNCB = 1;
!     }
  #ifdef DJGPP
!     dos_ncb = ((smb_ncb_t *)ncbp)->dos_ncb;
  #endif /* DJGPP */
   
!     memset((char *)ncbp, 0, sizeof(NCB));
  
!     extra = 2 * (*inp->wctp);	/* space used by parms, in bytes */
!     tp = inp->wctp + 1+ extra;	/* points to count of data bytes */
!     extra += tp[0] + (tp[1]<<8);
!     extra += ((unsigned int)inp->wctp - (unsigned int)inp->data);	/* distance to last wct field */
!     extra += 3;			/* wct and length fields */
!         
!     ncbp->ncb_length = extra;	/* bytes to send */
!     ncbp->ncb_lsn = (unsigned char) vcp->lsn;	/* vc to use */
!     ncbp->ncb_lana_num = vcp->lana;
!     ncbp->ncb_command = NCBSEND;	/* op means send data */
  #ifndef DJGPP
!     ncbp->ncb_buffer = (char *) inp;/* packet */
!     code = Netbios(ncbp);
  #else /* DJGPP */
!     ncbp->ncb_buffer = inp->dos_pkt;/* packet */
!     ((smb_ncb_t*)ncbp)->orig_pkt = inp;
  
!     /* copy header information from virtual to DOS address space */
!     dosmemput((char*)inp, SMB_PACKETSIZE, inp->dos_pkt);
!     code = Netbios(ncbp, dos_ncb);
  #endif /* !DJGPP */
          
!     if (code != 0)
!         osi_Log1(smb_logp, "SendPacket failure code %d", code);
  
!     if (localNCB)
!         FreeNCB(ncbp);
  }
  
  void smb_MapNTError(long code, unsigned long *NTStatusp)
  {
!     unsigned long NTStatus;
  
!     /* map CM_ERROR_* errors to NT 32-bit status codes */
      /* NT Status codes are listed in ntstatus.h not winerror.h */
!     if (code == CM_ERROR_NOSUCHCELL) {
!         NTStatus = 0xC000000FL;	/* No such file */
!     }
!     else if (code == CM_ERROR_NOSUCHVOLUME) {
!         NTStatus = 0xC000000FL;	/* No such file */
!     }
!     else if (code == CM_ERROR_TIMEDOUT) {
!         NTStatus = 0xC00000CFL;	/* Sharing Paused */
!     }
!     else if (code == CM_ERROR_RETRY) {
!         NTStatus = 0xC000022DL;	/* Retry */
!     }
!     else if (code == CM_ERROR_NOACCESS) {
!         NTStatus = 0xC0000022L;	/* Access denied */
!     }
!     else if (code == CM_ERROR_READONLY) {
!         NTStatus = 0xC00000A2L;	/* Write protected */
!     }	
!     else if (code == CM_ERROR_NOSUCHFILE) {
!         NTStatus = 0xC000000FL;	/* No such file */
!     }
!     else if (code == CM_ERROR_NOSUCHPATH) {
!         NTStatus = 0xC000003AL;	/* Object path not found */
!     }		
!     else if (code == CM_ERROR_TOOBIG) {
!         NTStatus = 0xC000007BL;	/* Invalid image format */
!     }
!     else if (code == CM_ERROR_INVAL) {
!         NTStatus = 0xC000000DL;	/* Invalid parameter */
!     }
!     else if (code == CM_ERROR_BADFD) {
!         NTStatus = 0xC0000008L;	/* Invalid handle */
!     }
!     else if (code == CM_ERROR_BADFDOP) {
!         NTStatus = 0xC0000022L;	/* Access denied */
!     }
!     else if (code == CM_ERROR_EXISTS) {
!         NTStatus = 0xC0000035L;	/* Object name collision */
!     }
!     else if (code == CM_ERROR_NOTEMPTY) {
!         NTStatus = 0xC0000101L;	/* Directory not empty */
!     }	
!     else if (code == CM_ERROR_CROSSDEVLINK) {
!         NTStatus = 0xC00000D4L;	/* Not same device */
!     }
!     else if (code == CM_ERROR_NOTDIR) {
!         NTStatus = 0xC0000103L;	/* Not a directory */
!     }
!     else if (code == CM_ERROR_ISDIR) {
!         NTStatus = 0xC00000BAL;	/* File is a directory */
!     }
!     else if (code == CM_ERROR_BADOP) {
  #ifdef COMMENT
!         /* I have no idea where this comes from */
!         NTStatus = 0xC09820FFL;	/* SMB no support */
  #else
!         NTStatus = 0xC00000BBL;     /* Not supported */
! #endif /* COMMENT */
!     }
!     else if (code == CM_ERROR_BADSHARENAME) {
!         NTStatus = 0xC00000CCL;	/* Bad network name */
!     }
!     else if (code == CM_ERROR_NOIPC) {
! #ifdef COMMENT
!         NTStatus = 0xC0000022L;	/* Access Denied */
! #else   
!         NTStatus = 0xC000013DL; /* Remote Resources */
! #endif
!     }
!     else if (code == CM_ERROR_CLOCKSKEW) {
!         NTStatus = 0xC0000133L;	/* Time difference at DC */
!     }
!     else if (code == CM_ERROR_BADTID) {
!         NTStatus = 0xC0982005L;	/* SMB bad TID */
!     }
!     else if (code == CM_ERROR_USESTD) {
!         NTStatus = 0xC09820FBL;	/* SMB use standard */
!     }
!     else if (code == CM_ERROR_QUOTA) {
! #ifdef COMMENT
!         NTStatus = 0xC0000044L;	/* Quota exceeded */
! #else
!         NTStatus = 0xC000007FL;	/* Disk full */
  #endif
!     }
!     else if (code == CM_ERROR_SPACE) {
!         NTStatus = 0xC000007FL;	/* Disk full */
!     }
!     else if (code == CM_ERROR_ATSYS) {
!         NTStatus = 0xC0000033L;	/* Object name invalid */
!     }
!     else if (code == CM_ERROR_BADNTFILENAME) {
!         NTStatus = 0xC0000033L;	/* Object name invalid */
!     }
!     else if (code == CM_ERROR_WOULDBLOCK) {
!         NTStatus = 0xC0000055L;	/* Lock not granted */
!     }
!     else if (code == CM_ERROR_PARTIALWRITE) {
!         NTStatus = 0xC000007FL;	/* Disk full */
!     }
!     else if (code == CM_ERROR_BUFFERTOOSMALL) {
!         NTStatus = 0xC0000023L;	/* Buffer too small */
!     }
      else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
!         NTStatus = 0xC0000035L;	/* Object name collision */
!     }   
!     else if (code == CM_ERROR_BADPASSWORD) {
!         NTStatus = 0xC000006DL; /* unknown username or bad password */
!     }
!     else if (code == CM_ERROR_BADLOGONTYPE) {
!         NTStatus = 0xC000015BL; /* logon type not granted */
!     }
!     else if (code == CM_ERROR_GSSCONTINUE) {
!         NTStatus = 0xC0000016L; /* more processing required */
!     }
!     else {
!         NTStatus = 0xC0982001L;	/* SMB non-specific error */
      }
  
!     *NTStatusp = NTStatus;
!     osi_Log2(smb_logp, "SMB SEND code %lX as NT %lX", code, NTStatus);
! }       
  
  void smb_MapCoreError(long code, smb_vc_t *vcp, unsigned short *scodep,
!                       unsigned char *classp)
  {
!     unsigned char class;
!     unsigned short error;
  
!     /* map CM_ERROR_* errors to SMB errors */
!     if (code == CM_ERROR_NOSUCHCELL) {
!         class = 1;
!         error = 3;	/* bad path */
!     }
!     else if (code == CM_ERROR_NOSUCHVOLUME) {
!         class = 1;
!         error = 3;	/* bad path */
!     }
!     else if (code == CM_ERROR_TIMEDOUT) {
!         class = 2;
!         error = 81;	/* server is paused */
!     }
!     else if (code == CM_ERROR_RETRY) {
!         class = 2;	/* shouldn't happen */
!         error = 1;
!     }
!     else if (code == CM_ERROR_NOACCESS) {
!         class = 2;
!         error = 4;	/* bad access */
!     }
!     else if (code == CM_ERROR_READONLY) {
!         class = 3;
!         error = 19;	/* read only */
!     }
!     else if (code == CM_ERROR_NOSUCHFILE) {
!         class = 1;
!         error = 2;	/* ENOENT! */
!     }
!     else if (code == CM_ERROR_NOSUCHPATH) {
!         class = 1;
!         error = 3;	/* Bad path */
!     }
!     else if (code == CM_ERROR_TOOBIG) {
!         class = 1;
!         error = 11;	/* bad format */
!     }
!     else if (code == CM_ERROR_INVAL) {
!         class = 2;	/* server non-specific error code */
!         error = 1;
!     }
!     else if (code == CM_ERROR_BADFD) {
!         class = 1;
!         error = 6;	/* invalid file handle */
!     }
!     else if (code == CM_ERROR_BADFDOP) {
!         class = 1;	/* invalid op on FD */
!         error = 5;
!     }
!     else if (code == CM_ERROR_EXISTS) {
!         class = 1;
!         error = 80;	/* file already exists */
!     }
!     else if (code == CM_ERROR_NOTEMPTY) {
!         class = 1;
!         error = 5;	/* delete directory not empty */
!     }
!     else if (code == CM_ERROR_CROSSDEVLINK) {
!         class = 1;
!         error = 17;	/* EXDEV */
!     }
!     else if (code == CM_ERROR_NOTDIR) {
!         class = 1;	/* bad path */
!         error = 3;
!     }
!     else if (code == CM_ERROR_ISDIR) {
!         class = 1;	/* access denied; DOS doesn't have a good match */
!         error = 5;
!     }       
!     else if (code == CM_ERROR_BADOP) {
!         class = 2;
!         error = 65535;
!     }
!     else if (code == CM_ERROR_BADSHARENAME) {
!         class = 2;
!         error = 6;
!     }
!     else if (code == CM_ERROR_NOIPC) {
!         class = 2;
!         error = 4; /* bad access */
!     }
!     else if (code == CM_ERROR_CLOCKSKEW) {
!         class = 1;	/* invalid function */
!         error = 1;
!     }
!     else if (code == CM_ERROR_BADTID) {
!         class = 2;
!         error = 5;
!     }
!     else if (code == CM_ERROR_USESTD) {
!         class = 2;
!         error = 251;
!     }
!     else if (code == CM_ERROR_REMOTECONN) {
!         class = 2;
!         error = 82;
!     }
!     else if (code == CM_ERROR_QUOTA) {
!         if (vcp->flags & SMB_VCFLAG_USEV3) {
!             class = 3;
!             error = 39;	/* disk full */
!         }
!         else {
!             class = 1;
!             error = 5;	/* access denied */
!         }
!     }
!     else if (code == CM_ERROR_SPACE) {
!         if (vcp->flags & SMB_VCFLAG_USEV3) {
!             class = 3;
!             error = 39;	/* disk full */
!         }
!         else {
!             class = 1;
!             error = 5;	/* access denied */
!         }
!     }
!     else if (code == CM_ERROR_PARTIALWRITE) {
!         class = 3;
!         error = 39;	/* disk full */
!     }
!     else if (code == CM_ERROR_ATSYS) {
!         class = 1;
!         error = 2;	/* ENOENT */
!     }
!     else if (code == CM_ERROR_WOULDBLOCK) {
!         class = 1;
!         error = 33;	/* lock conflict */
!     }
!     else if (code == CM_ERROR_NOFILES) {
!         class = 1;
!         error = 18;	/* no files in search */
!     }
!     else if (code == CM_ERROR_RENAME_IDENTICAL) {
!         class = 1;
!         error = 183;     /* Samba uses this */
!     }
!     else if (code == CM_ERROR_BADPASSWORD || code == CM_ERROR_BADLOGONTYPE) {
!         /* we don't have a good way of reporting CM_ERROR_BADLOGONTYPE */
!         class = 2;
!         error = 2; /* bad password */
!     }
!     else {
!         class = 2;
!         error = 1;
!     }
! 
!     *scodep = error;
!     *classp = class;
!     osi_Log3(smb_logp, "SMB SEND code %lX as SMB %d: %d", code, class, error);
! }       
  
  long smb_SendCoreBadOp(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
!     osi_Log0(smb_logp,"SendCoreBadOp - NOT_SUPPORTED");
!     return CM_ERROR_BADOP;
  }
  
  long smb_ReceiveCoreEcho(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
!     unsigned short EchoCount, i;
!     char *data, *outdata;
!     int dataSize;
! 
!     EchoCount = (unsigned short) smb_GetSMBParm(inp, 0);
! 
!     for (i=1; i<=EchoCount; i++) {
!         data = smb_GetSMBData(inp, &dataSize);
!         smb_SetSMBParm(outp, 0, i);
!         smb_SetSMBDataLength(outp, dataSize);
!         outdata = smb_GetSMBData(outp, NULL);
!         memcpy(outdata, data, dataSize);
!         smb_SendPacket(vcp, outp);
!     }
  
!     return 0;
  }
  
  long smb_ReceiveCoreReadRaw(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
!     osi_hyper_t offset;
!     long count, minCount, finalCount;
!     unsigned short fd;
!     smb_fid_t *fidp;
!     long code = 0;
!     cm_user_t *userp = NULL;
      NCB *ncbp;
      int rc;
  #ifndef DJGPP
***************
*** 2458,2492 ****
      dos_ptr dos_ncb;
  #endif /* DJGPP */
  
! 	rawBuf = NULL;
! 	finalCount = 0;
  
! 	fd = smb_GetSMBParm(inp, 0);
! 	count = smb_GetSMBParm(inp, 3);
! 	minCount = smb_GetSMBParm(inp, 4);
! 	offset.HighPart = 0;	/* too bad */
! 	offset.LowPart = smb_GetSMBParm(inp, 1) | (smb_GetSMBParm(inp, 2) << 16);
  
! 	osi_Log3(smb_logp, "smb_ReceieveCoreReadRaw fd %d, off 0x%x, size 0x%x",
               fd, offset.LowPart, count);
  
! 	fidp = smb_FindFID(vcp, fd, 0);
! 	if (!fidp)
! 		goto send1;
! 
! 	lock_ObtainMutex(&smb_RawBufLock);
! 	if (smb_RawBufs) {
! 		/* Get a raw buf, from head of list */
! 		rawBuf = smb_RawBufs;
  #ifndef DJGPP
! 		smb_RawBufs = *(char **)smb_RawBufs;
  #else /* DJGPP */
          smb_RawBufs = _farpeekl(_dos_ds, smb_RawBufs);
  #endif /* !DJGPP */
! 	}
! 	lock_ReleaseMutex(&smb_RawBufLock);
! 	if (!rawBuf)
! 		goto send1a;
  
      if (fidp->flags & SMB_FID_IOCTL)
      {
--- 2484,2518 ----
      dos_ptr dos_ncb;
  #endif /* DJGPP */
  
!     rawBuf = NULL;
!     finalCount = 0;
  
!     fd = smb_GetSMBParm(inp, 0);
!     count = smb_GetSMBParm(inp, 3);
!     minCount = smb_GetSMBParm(inp, 4);
!     offset.HighPart = 0;	/* too bad */
!     offset.LowPart = smb_GetSMBParm(inp, 1) | (smb_GetSMBParm(inp, 2) << 16);
  
!     osi_Log3(smb_logp, "smb_ReceieveCoreReadRaw fd %d, off 0x%x, size 0x%x",
               fd, offset.LowPart, count);
  
!     fidp = smb_FindFID(vcp, fd, 0);
!     if (!fidp)
!         goto send1;
! 
!     lock_ObtainMutex(&smb_RawBufLock);
!     if (smb_RawBufs) {
!         /* Get a raw buf, from head of list */
!         rawBuf = smb_RawBufs;
  #ifndef DJGPP
!         smb_RawBufs = *(char **)smb_RawBufs;
  #else /* DJGPP */
          smb_RawBufs = _farpeekl(_dos_ds, smb_RawBufs);
  #endif /* !DJGPP */
!     }
!     lock_ReleaseMutex(&smb_RawBufLock);
!     if (!rawBuf)
!         goto send1a;
  
      if (fidp->flags & SMB_FID_IOCTL)
      {
***************
*** 2515,2687 ****
      userp = smb_GetUser(vcp, inp);
  
  #ifndef DJGPP
! 	code = smb_ReadData(fidp, &offset, count, rawBuf, userp, &finalCount);
  #else /* DJGPP */
      /* have to give ReadData flag so it will treat buffer as DOS mem. */
      code = smb_ReadData(fidp, &offset, count, (unsigned char *)rawBuf,
                          userp, &finalCount, TRUE /* rawFlag */);
  #endif /* !DJGPP */
  
! 	if (code != 0)
! 		goto send;
  
    send:
      cm_ReleaseUser(userp);
  
    send1a:
! 	smb_ReleaseFID(fidp);
  
    send1:
! 	ncbp = outp->ncbp;
  #ifdef DJGPP
      dos_ncb = ((smb_ncb_t *)ncbp)->dos_ncb;
  #endif /* DJGPP */
! 	memset((char *)ncbp, 0, sizeof(NCB));
  
! 	ncbp->ncb_length = (unsigned short) finalCount;
! 	ncbp->ncb_lsn = (unsigned char) vcp->lsn;
! 	ncbp->ncb_lana_num = vcp->lana;
! 	ncbp->ncb_command = NCBSEND;
! 	ncbp->ncb_buffer = rawBuf;
  
  #ifndef DJGPP
! 	code = Netbios(ncbp);
  #else /* DJGPP */
! 	code = Netbios(ncbp, dos_ncb);
  #endif /* !DJGPP */
! 	if (code != 0)
! 		osi_Log1(smb_logp, "ReadRaw send failure code %d", code);
  
! 	if (rawBuf) {
! 		/* Give back raw buffer */
! 		lock_ObtainMutex(&smb_RawBufLock);
  #ifndef DJGPP
! 		*((char **) rawBuf) = smb_RawBufs;
  #else /* DJGPP */
          _farpokel(_dos_ds, rawBuf, smb_RawBufs);
  #endif /* !DJGPP */
  
! 		smb_RawBufs = rawBuf;
! 		lock_ReleaseMutex(&smb_RawBufLock);
! 	}
  
! 	return 0;
  }
  
  long smb_ReceiveCoreLockRecord(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
! 	return 0;
  }
  
  long smb_ReceiveCoreUnlockRecord(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
! 	return 0;
  }
  
  long smb_ReceiveNegotiate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
! 	char *namep;
      char *datap;
! 	int coreProtoIndex;
! 	int v3ProtoIndex;
! 	int NTProtoIndex;
! 	int protoIndex;			        /* index we're using */
! 	int namex;
! 	int dbytes;
! 	int entryLength;
! 	int tcounter;
! 	char protocol_array[10][1024];  /* protocol signature of the client */
      int caps;                       /* capabilities */
      time_t unixTime;
! 	time_t dosTime;
! 	TIME_ZONE_INFORMATION tzi;
  
      osi_Log1(smb_logp, "SMB receive negotiate; %d + 1 ongoing ops",
  			 ongoingOps - 1);
! 	if (!isGateway) {
! 		if (active_vcp) {
! 			DWORD now = GetCurrentTime();
! 			if (now - last_msg_time >= 30000
! 				&& now - last_msg_time <= 90000) {
! 				osi_Log1(smb_logp,
! 						 "Setting dead_vcp %x", active_vcp);
                  if (dead_vcp) {
                      smb_ReleaseVC(dead_vcp);
                      osi_Log1(smb_logp,
!                               "Previous dead_vcp %x", dead_vcp);
                  }
                  smb_HoldVC(active_vcp);
! 				dead_vcp = active_vcp;
! 				dead_vcp->flags |= SMB_VCFLAG_ALREADYDEAD;
! 			}
! 		}
! 	}
! 
! 	inp->flags |= SMB_PACKETFLAG_PROFILE_UPDATE_OK;
! 
! 	namep = smb_GetSMBData(inp, &dbytes);
! 	namex = 0;
! 	tcounter = 0;
! 	coreProtoIndex = -1;		/* not found */
! 	v3ProtoIndex = -1;
! 	NTProtoIndex = -1;
! 	while(namex < dbytes) {
! 		osi_Log1(smb_logp, "Protocol %s",
! 				 osi_LogSaveString(smb_logp, namep+1));
! 		strcpy(protocol_array[tcounter], namep+1);
! 
! 		/* namep points at the first protocol, or really, a 0x02
! 		 * byte preceding the null-terminated ASCII name.
! 		 */
! 		if (strcmp("PC NETWORK PROGRAM 1.0", namep+1) == 0) {
! 			coreProtoIndex = tcounter;
! 		}	
! 		else if (smb_useV3 && strcmp("LM1.2X002", namep+1) == 0) {
! 			v3ProtoIndex = tcounter;
! 		}
! 		else if (smb_useV3 && strcmp("NT LM 0.12", namep+1) == 0) {
! 			NTProtoIndex = tcounter;
! 		}
  
! 		/* compute size of protocol entry */
! 		entryLength = strlen(namep+1);
          entryLength += 2;	/* 0x02 bytes and null termination */
!                 
          /* advance over this protocol entry */
! 		namex += entryLength;
          namep += entryLength;
          tcounter++;		/* which proto entry we're looking at */
! 	}
  
! 	if (NTProtoIndex != -1) {
! 		protoIndex = NTProtoIndex;
! 		vcp->flags |= (SMB_VCFLAG_USENT | SMB_VCFLAG_USEV3);
! 	}
! 	else if (v3ProtoIndex != -1) {
! 		protoIndex = v3ProtoIndex;
! 		vcp->flags |= SMB_VCFLAG_USEV3;
! 	}	
! 	else if (coreProtoIndex != -1) {
! 		protoIndex = coreProtoIndex;
! 		vcp->flags |= SMB_VCFLAG_USECORE;
! 	}	
! 	else protoIndex = -1;
! 
! 	if (protoIndex == -1)
! 		return CM_ERROR_INVAL;
! 	else if (NTProtoIndex != -1) {
          smb_SetSMBParm(outp, 0, protoIndex);
! 		if (smb_authType != SMB_AUTH_NONE) {
! 			smb_SetSMBParmByte(outp, 1,
! 				NEGOTIATE_SECURITY_USER_LEVEL |
! 				NEGOTIATE_SECURITY_CHALLENGE_RESPONSE);	/* user level security, challenge response */
! 		} else {
              smb_SetSMBParmByte(outp, 1, 0); /* share level auth with plaintext password. */
! 		}
          smb_SetSMBParm(outp, 1, smb_maxMpxRequests);	/* max multiplexed requests */
          smb_SetSMBParm(outp, 2, smb_maxVCPerServer);	/* max VCs per consumer/server connection */
          smb_SetSMBParmLong(outp, 3, SMB_PACKETSIZE);    /* xmit buffer size */
! 		smb_SetSMBParmLong(outp, 5, SMB_MAXRAWSIZE);	/* raw buffer size */
          /* The session key is not a well documented field however most clients
           * will echo back the session key to the server.  Currently we are using
           * the same value for all sessions.  We should generate a random value
--- 2541,2717 ----
      userp = smb_GetUser(vcp, inp);
  
  #ifndef DJGPP
!     code = smb_ReadData(fidp, &offset, count, rawBuf, userp, &finalCount);
  #else /* DJGPP */
      /* have to give ReadData flag so it will treat buffer as DOS mem. */
      code = smb_ReadData(fidp, &offset, count, (unsigned char *)rawBuf,
                          userp, &finalCount, TRUE /* rawFlag */);
  #endif /* !DJGPP */
  
!     if (code != 0)
!         goto send;
  
    send:
      cm_ReleaseUser(userp);
  
    send1a:
!     smb_ReleaseFID(fidp);
  
    send1:
!     ncbp = outp->ncbp;
  #ifdef DJGPP
      dos_ncb = ((smb_ncb_t *)ncbp)->dos_ncb;
  #endif /* DJGPP */
!     memset((char *)ncbp, 0, sizeof(NCB));
  
!     ncbp->ncb_length = (unsigned short) finalCount;
!     ncbp->ncb_lsn = (unsigned char) vcp->lsn;
!     ncbp->ncb_lana_num = vcp->lana;
!     ncbp->ncb_command = NCBSEND;
!     ncbp->ncb_buffer = rawBuf;
  
  #ifndef DJGPP
!     code = Netbios(ncbp);
  #else /* DJGPP */
!     code = Netbios(ncbp, dos_ncb);
  #endif /* !DJGPP */
!     if (code != 0)
!         osi_Log1(smb_logp, "ReadRaw send failure code %d", code);
  
!     if (rawBuf) {
!         /* Give back raw buffer */
!         lock_ObtainMutex(&smb_RawBufLock);
  #ifndef DJGPP
!         *((char **) rawBuf) = smb_RawBufs;
  #else /* DJGPP */
          _farpokel(_dos_ds, rawBuf, smb_RawBufs);
  #endif /* !DJGPP */
  
!         smb_RawBufs = rawBuf;
!         lock_ReleaseMutex(&smb_RawBufLock);
!     }
  
!     return 0;
  }
  
  long smb_ReceiveCoreLockRecord(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
!     osi_Log1(smb_logp, "SMB receive core lock record (not implemented); %d + 1 ongoing ops",
! 			 ongoingOps - 1);
!     return 0;
  }
  
  long smb_ReceiveCoreUnlockRecord(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
!     osi_Log1(smb_logp, "SMB receive core unlock record (not implemented); %d + 1 ongoing ops",
! 			 ongoingOps - 1);
!     return 0;
  }
  
  long smb_ReceiveNegotiate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
!     char *namep;
      char *datap;
!     int coreProtoIndex;
!     int v3ProtoIndex;
!     int NTProtoIndex;
!     int protoIndex;			        /* index we're using */
!     int namex;
!     int dbytes;
!     int entryLength;
!     int tcounter;
!     char protocol_array[10][1024];  /* protocol signature of the client */
      int caps;                       /* capabilities */
      time_t unixTime;
!     time_t dosTime;
!     TIME_ZONE_INFORMATION tzi;
  
      osi_Log1(smb_logp, "SMB receive negotiate; %d + 1 ongoing ops",
  			 ongoingOps - 1);
!     if (!isGateway) {
!         if (active_vcp) {
!             DWORD now = GetCurrentTime();
!             if (now - last_msg_time >= 30000
!                  && now - last_msg_time <= 90000) {
!                 osi_Log1(smb_logp,
!                           "Setting dead_vcp %x", active_vcp);
                  if (dead_vcp) {
                      smb_ReleaseVC(dead_vcp);
                      osi_Log1(smb_logp,
!                              "Previous dead_vcp %x", dead_vcp);
                  }
                  smb_HoldVC(active_vcp);
!                 dead_vcp = active_vcp;
!                 dead_vcp->flags |= SMB_VCFLAG_ALREADYDEAD;
!             }
!         }
!     }
! 
!     inp->flags |= SMB_PACKETFLAG_PROFILE_UPDATE_OK;
! 
!     namep = smb_GetSMBData(inp, &dbytes);
!     namex = 0;
!     tcounter = 0;
!     coreProtoIndex = -1;		/* not found */
!     v3ProtoIndex = -1;
!     NTProtoIndex = -1;
!     while(namex < dbytes) {
!         osi_Log1(smb_logp, "Protocol %s",
!                   osi_LogSaveString(smb_logp, namep+1));
!         strcpy(protocol_array[tcounter], namep+1);
! 
!         /* namep points at the first protocol, or really, a 0x02
!          * byte preceding the null-terminated ASCII name.
!          */
!         if (strcmp("PC NETWORK PROGRAM 1.0", namep+1) == 0) {
!             coreProtoIndex = tcounter;
!         }	
!         else if (smb_useV3 && strcmp("LM1.2X002", namep+1) == 0) {
!             v3ProtoIndex = tcounter;
!         }
!         else if (smb_useV3 && strcmp("NT LM 0.12", namep+1) == 0) {
!             NTProtoIndex = tcounter;
!         }
  
!         /* compute size of protocol entry */
!         entryLength = strlen(namep+1);
          entryLength += 2;	/* 0x02 bytes and null termination */
! 
          /* advance over this protocol entry */
!         namex += entryLength;
          namep += entryLength;
          tcounter++;		/* which proto entry we're looking at */
!     }
! 
!     if (NTProtoIndex != -1) {
!         protoIndex = NTProtoIndex;
!         vcp->flags |= (SMB_VCFLAG_USENT | SMB_VCFLAG_USEV3);
!     }
!     else if (v3ProtoIndex != -1) {
!         protoIndex = v3ProtoIndex;
!         vcp->flags |= SMB_VCFLAG_USEV3;
!     }	
!     else if (coreProtoIndex != -1) {
!         protoIndex = coreProtoIndex;
!         vcp->flags |= SMB_VCFLAG_USECORE;
!     }	
!     else protoIndex = -1;
  
!     if (protoIndex == -1)
!         return CM_ERROR_INVAL;
!     else if (NTProtoIndex != -1) {
          smb_SetSMBParm(outp, 0, protoIndex);
!         if (smb_authType != SMB_AUTH_NONE) {
!             smb_SetSMBParmByte(outp, 1,
!                                NEGOTIATE_SECURITY_USER_LEVEL |
!                                NEGOTIATE_SECURITY_CHALLENGE_RESPONSE);	/* user level security, challenge response */
!         } else {
              smb_SetSMBParmByte(outp, 1, 0); /* share level auth with plaintext password. */
!         }
          smb_SetSMBParm(outp, 1, smb_maxMpxRequests);	/* max multiplexed requests */
          smb_SetSMBParm(outp, 2, smb_maxVCPerServer);	/* max VCs per consumer/server connection */
          smb_SetSMBParmLong(outp, 3, SMB_PACKETSIZE);    /* xmit buffer size */
!         smb_SetSMBParmLong(outp, 5, SMB_MAXRAWSIZE);	/* raw buffer size */
          /* The session key is not a well documented field however most clients
           * will echo back the session key to the server.  Currently we are using
           * the same value for all sessions.  We should generate a random value
***************
*** 2689,2817 ****
           */
          smb_SetSMBParm(outp, 7, 1);	/* next 2: session key */
          smb_SetSMBParm(outp, 8, 1);
! 		/* 
! 		 * Tried changing the capabilities to support for W2K - defect 117695
! 		 * Maybe something else needs to be changed here?
! 		 */
! 		/*
! 		  if (isWindows2000) 
! 		  smb_SetSMBParmLong(outp, 9, 0x43fd);
! 		  else 
! 		  smb_SetSMBParmLong(outp, 9, 0x251);
! 		  */
! 		/* Capabilities: *
! 		 * 32-bit error codes *
! 		 * and NT Find *
! 		 * and NT SMB's *
! 		 * and raw mode */
          caps = NTNEGOTIATE_CAPABILITY_NTSTATUS |
! 			   NTNEGOTIATE_CAPABILITY_NTFIND |
                 NTNEGOTIATE_CAPABILITY_RAWMODE |
! 			   NTNEGOTIATE_CAPABILITY_NTSMB;
  
          if ( smb_authType == SMB_AUTH_EXTENDED )
              caps |= NTNEGOTIATE_CAPABILITY_EXTENDED_SECURITY;
  
          smb_SetSMBParmLong(outp, 9, caps);
! 		time(&unixTime);
! 		smb_SearchTimeFromUnixTime(&dosTime, unixTime);
! 		smb_SetSMBParmLong(outp, 11, LOWORD(dosTime));/* server time */
! 		smb_SetSMBParmLong(outp, 13, HIWORD(dosTime));/* server date */
! 
! 		GetTimeZoneInformation(&tzi);
! 		smb_SetSMBParm(outp, 15, (unsigned short) tzi.Bias);	/* server tzone */
! 
! 		if (smb_authType == SMB_AUTH_NTLM) {
! 			smb_SetSMBParmByte(outp, 16, MSV1_0_CHALLENGE_LENGTH);/* Encryption key length */
! 			smb_SetSMBDataLength(outp, MSV1_0_CHALLENGE_LENGTH + smb_ServerDomainNameLength);
! 			/* paste in encryption key */
! 			datap = smb_GetSMBData(outp, NULL);
! 			memcpy(datap,vcp->encKey,MSV1_0_CHALLENGE_LENGTH);
! 			/* and the faux domain name */
! 			strcpy(datap + MSV1_0_CHALLENGE_LENGTH,smb_ServerDomainName);
! 		} else if ( smb_authType == SMB_AUTH_EXTENDED ) {
              void * secBlob;
! 			int secBlobLength;
  
! 			smb_SetSMBParmByte(outp, 16, 0); /* Encryption key length */
  
! 			smb_NegotiateExtendedSecurity(&secBlob, &secBlobLength);
  
! 			smb_SetSMBDataLength(outp, secBlobLength + sizeof(smb_ServerGUID));
  			
! 			datap = smb_GetSMBData(outp, NULL);
! 			memcpy(datap, &smb_ServerGUID, sizeof(smb_ServerGUID));
  
! 			if (secBlob) {
! 				datap += sizeof(smb_ServerGUID);
! 				memcpy(datap, secBlob, secBlobLength);
! 				free(secBlob);
! 			}
          } else {
! 			smb_SetSMBParmByte(outp, 16, 0); /* Encryption key length */
! 			smb_SetSMBDataLength(outp, 0);   /* Perhaps we should specify 8 bytes anyway */
! 		}
! 	}
! 	else if (v3ProtoIndex != -1) {
! 		smb_SetSMBParm(outp, 0, protoIndex);
  
          /* NOTE: Extended authentication cannot be negotiated with v3
           * therefore we fail over to NTLM 
           */
          if (smb_authType == SMB_AUTH_NTLM || smb_authType == SMB_AUTH_EXTENDED) {
! 			smb_SetSMBParm(outp, 1,
! 				NEGOTIATE_SECURITY_USER_LEVEL |
! 				NEGOTIATE_SECURITY_CHALLENGE_RESPONSE);	/* user level security, challenge response */
! 		} else {
! 			smb_SetSMBParm(outp, 1, 0); /* share level auth with clear password */
! 		}
! 		smb_SetSMBParm(outp, 2, SMB_PACKETSIZE);
! 		smb_SetSMBParm(outp, 3, smb_maxMpxRequests);	/* max multiplexed requests */
! 		smb_SetSMBParm(outp, 4, smb_maxVCPerServer);	/* max VCs per consumer/server connection */
! 		smb_SetSMBParm(outp, 5, 0);	/* no support of block mode for read or write */
! 		smb_SetSMBParm(outp, 6, 1);	/* next 2: session key */
! 		smb_SetSMBParm(outp, 7, 1);
! 		time(&unixTime);
! 		smb_SearchTimeFromUnixTime(&dosTime, unixTime);
! 		smb_SetSMBParm(outp, 8, LOWORD(dosTime));	/* server time */
! 		smb_SetSMBParm(outp, 9, HIWORD(dosTime));	/* server date */
  
! 		GetTimeZoneInformation(&tzi);
! 		smb_SetSMBParm(outp, 10, (unsigned short) tzi.Bias);	/* server tzone */
  
          /* NOTE: Extended authentication cannot be negotiated with v3
           * therefore we fail over to NTLM 
           */
! 		if (smb_authType == SMB_AUTH_NTLM || smb_authType == SMB_AUTH_EXTENDED) {
! 			smb_SetSMBParm(outp, 11, MSV1_0_CHALLENGE_LENGTH);	/* encryption key length */
              smb_SetSMBParm(outp, 12, 0);	/* resvd */
! 			smb_SetSMBDataLength(outp, MSV1_0_CHALLENGE_LENGTH + smb_ServerDomainNameLength);	/* perhaps should specify 8 bytes anyway */
! 			datap = smb_GetSMBData(outp, NULL);
! 			/* paste in a new encryption key */
! 			memcpy(datap, vcp->encKey, MSV1_0_CHALLENGE_LENGTH);
! 			/* and the faux domain name */
! 			strcpy(datap + MSV1_0_CHALLENGE_LENGTH, smb_ServerDomainName);
! 		} else {
! 			smb_SetSMBParm(outp, 11, 0); /* encryption key length */
! 			smb_SetSMBParm(outp, 12, 0); /* resvd */
! 			smb_SetSMBDataLength(outp, 0);
! 		}
! 	}
! 	else if (coreProtoIndex != -1) {     /* not really supported anymore */
! 		smb_SetSMBParm(outp, 0, protoIndex);
! 		smb_SetSMBDataLength(outp, 0);
! 	}
! 	return 0;
  }
  
  void smb_Daemon(void *parmp)
  {
! 	afs_uint32 count = 0;
  
! 	while(1) {
! 		count++;
! 		thrd_Sleep(10000);
! 		if ((count % 72) == 0)	{	/* every five minutes */
              struct tm myTime;
              long old_localZero = smb_localZero;
  		 
--- 2719,2847 ----
           */
          smb_SetSMBParm(outp, 7, 1);	/* next 2: session key */
          smb_SetSMBParm(outp, 8, 1);
!         /* 
!          * Tried changing the capabilities to support for W2K - defect 117695
!          * Maybe something else needs to be changed here?
!          */
!         /*
!         if (isWindows2000) 
!         smb_SetSMBParmLong(outp, 9, 0x43fd);
!         else 
!         smb_SetSMBParmLong(outp, 9, 0x251);
!         */
!         /* Capabilities: *
!          * 32-bit error codes *
!          * and NT Find *
!          * and NT SMB's *
!          * and raw mode */
          caps = NTNEGOTIATE_CAPABILITY_NTSTATUS |
!                NTNEGOTIATE_CAPABILITY_NTFIND |
                 NTNEGOTIATE_CAPABILITY_RAWMODE |
!                NTNEGOTIATE_CAPABILITY_NTSMB;
  
          if ( smb_authType == SMB_AUTH_EXTENDED )
              caps |= NTNEGOTIATE_CAPABILITY_EXTENDED_SECURITY;
  
          smb_SetSMBParmLong(outp, 9, caps);
!         time(&unixTime);
!         smb_SearchTimeFromUnixTime(&dosTime, unixTime);
!         smb_SetSMBParmLong(outp, 11, LOWORD(dosTime));/* server time */
!         smb_SetSMBParmLong(outp, 13, HIWORD(dosTime));/* server date */
! 
!         GetTimeZoneInformation(&tzi);
!         smb_SetSMBParm(outp, 15, (unsigned short) tzi.Bias);	/* server tzone */
! 
!         if (smb_authType == SMB_AUTH_NTLM) {
!             smb_SetSMBParmByte(outp, 16, MSV1_0_CHALLENGE_LENGTH);/* Encryption key length */
!             smb_SetSMBDataLength(outp, MSV1_0_CHALLENGE_LENGTH + smb_ServerDomainNameLength);
!             /* paste in encryption key */
!             datap = smb_GetSMBData(outp, NULL);
!             memcpy(datap,vcp->encKey,MSV1_0_CHALLENGE_LENGTH);
!             /* and the faux domain name */
!             strcpy(datap + MSV1_0_CHALLENGE_LENGTH,smb_ServerDomainName);
!         } else if ( smb_authType == SMB_AUTH_EXTENDED ) {
              void * secBlob;
!             int secBlobLength;
  
!             smb_SetSMBParmByte(outp, 16, 0); /* Encryption key length */
  
!             smb_NegotiateExtendedSecurity(&secBlob, &secBlobLength);
  
!             smb_SetSMBDataLength(outp, secBlobLength + sizeof(smb_ServerGUID));
  			
!             datap = smb_GetSMBData(outp, NULL);
!             memcpy(datap, &smb_ServerGUID, sizeof(smb_ServerGUID));
  
!             if (secBlob) {
!                 datap += sizeof(smb_ServerGUID);
!                 memcpy(datap, secBlob, secBlobLength);
!                 free(secBlob);
!             }
          } else {
!             smb_SetSMBParmByte(outp, 16, 0); /* Encryption key length */
!             smb_SetSMBDataLength(outp, 0);   /* Perhaps we should specify 8 bytes anyway */
!         }
!     }
!     else if (v3ProtoIndex != -1) {
!         smb_SetSMBParm(outp, 0, protoIndex);
  
          /* NOTE: Extended authentication cannot be negotiated with v3
           * therefore we fail over to NTLM 
           */
          if (smb_authType == SMB_AUTH_NTLM || smb_authType == SMB_AUTH_EXTENDED) {
!             smb_SetSMBParm(outp, 1,
!                            NEGOTIATE_SECURITY_USER_LEVEL |
!                            NEGOTIATE_SECURITY_CHALLENGE_RESPONSE);	/* user level security, challenge response */
!         } else {
!             smb_SetSMBParm(outp, 1, 0); /* share level auth with clear password */
!         }
!         smb_SetSMBParm(outp, 2, SMB_PACKETSIZE);
!         smb_SetSMBParm(outp, 3, smb_maxMpxRequests);	/* max multiplexed requests */
!         smb_SetSMBParm(outp, 4, smb_maxVCPerServer);	/* max VCs per consumer/server connection */
!         smb_SetSMBParm(outp, 5, 0);	/* no support of block mode for read or write */
!         smb_SetSMBParm(outp, 6, 1);	/* next 2: session key */
!         smb_SetSMBParm(outp, 7, 1);
!         time(&unixTime);
!         smb_SearchTimeFromUnixTime(&dosTime, unixTime);
!         smb_SetSMBParm(outp, 8, LOWORD(dosTime));	/* server time */
!         smb_SetSMBParm(outp, 9, HIWORD(dosTime));	/* server date */
  
!         GetTimeZoneInformation(&tzi);
!         smb_SetSMBParm(outp, 10, (unsigned short) tzi.Bias);	/* server tzone */
  
          /* NOTE: Extended authentication cannot be negotiated with v3
           * therefore we fail over to NTLM 
           */
!         if (smb_authType == SMB_AUTH_NTLM || smb_authType == SMB_AUTH_EXTENDED) {
!             smb_SetSMBParm(outp, 11, MSV1_0_CHALLENGE_LENGTH);	/* encryption key length */
              smb_SetSMBParm(outp, 12, 0);	/* resvd */
!             smb_SetSMBDataLength(outp, MSV1_0_CHALLENGE_LENGTH + smb_ServerDomainNameLength);	/* perhaps should specify 8 bytes anyway */
!             datap = smb_GetSMBData(outp, NULL);
!             /* paste in a new encryption key */
!             memcpy(datap, vcp->encKey, MSV1_0_CHALLENGE_LENGTH);
!             /* and the faux domain name */
!             strcpy(datap + MSV1_0_CHALLENGE_LENGTH, smb_ServerDomainName);
!         } else {
!             smb_SetSMBParm(outp, 11, 0); /* encryption key length */
!             smb_SetSMBParm(outp, 12, 0); /* resvd */
!             smb_SetSMBDataLength(outp, 0);
!         }
!     }
!     else if (coreProtoIndex != -1) {     /* not really supported anymore */
!         smb_SetSMBParm(outp, 0, protoIndex);
!         smb_SetSMBDataLength(outp, 0);
!     }
!     return 0;
  }
  
  void smb_Daemon(void *parmp)
  {
!     afs_uint32 count = 0;
  
!     while(1) {
!         count++;
!         thrd_Sleep(10000);
!         if ((count % 72) == 0)	{	/* every five minutes */
              struct tm myTime;
              long old_localZero = smb_localZero;
  		 
***************
*** 2832,2990 ****
                  cm_noteLocalMountPointChange();
  #endif
          }
! 		/* XXX GC dir search entries */
! 	}
  }
  
  void smb_WaitingLocksDaemon()
  {
! 	smb_waitingLock_t *wL, *nwL;
! 	int first;
! 	smb_vc_t *vcp;
! 	smb_packet_t *inp, *outp;
! 	NCB *ncbp;
! 	long code = 0;
! 
! 	while(1) {
! 		lock_ObtainWrite(&smb_globalLock);
! 		nwL = smb_allWaitingLocks;
! 		if (nwL == NULL) {
! 			osi_SleepW((long)&smb_allWaitingLocks, &smb_globalLock);
! 			thrd_Sleep(1000);
! 			continue;
! 		}
! 		else first = 1;
! 		do {
! 			if (first)
! 				first = 0;
! 			else
! 				lock_ObtainWrite(&smb_globalLock);
! 			wL = nwL;
! 			nwL = (smb_waitingLock_t *) osi_QNext(&wL->q);
! 			lock_ReleaseWrite(&smb_globalLock);
! 			code = cm_RetryLock((cm_file_lock_t *) wL->lockp,
! 								wL->vcp->flags & SMB_VCFLAG_ALREADYDEAD);
! 			if (code == CM_ERROR_WOULDBLOCK) {
! 				/* no progress */
! 				if (wL->timeRemaining != 0xffffffff
! 				    && (wL->timeRemaining -= 1000) < 0)
! 					goto endWait;
! 				continue;
! 			}
! 		  endWait:
! 			vcp = wL->vcp;
! 			inp = wL->inp;
! 			outp = wL->outp;
! 			ncbp = GetNCB();
! 			ncbp->ncb_length = inp->ncb_length;
! 			inp->spacep = cm_GetSpace();
! 
! 			/* Remove waitingLock from list */
! 			lock_ObtainWrite(&smb_globalLock);
! 			osi_QRemove((osi_queue_t **)&smb_allWaitingLocks,
! 				    &wL->q);
! 			lock_ReleaseWrite(&smb_globalLock);
! 
! 			/* Resume packet processing */
! 			if (code == 0)
! 				smb_SetSMBDataLength(outp, 0);
! 			outp->flags |= SMB_PACKETFLAG_SUSPENDED;
! 			outp->resumeCode = code;
! 			outp->ncbp = ncbp;
! 			smb_DispatchPacket(vcp, inp, outp, ncbp, NULL);
! 
! 			/* Clean up */
! 			cm_FreeSpace(inp->spacep);
! 			smb_FreePacket(inp);
! 			smb_FreePacket(outp);
! 			FreeNCB(ncbp);
! 			free(wL);
! 		} while (nwL);
! 		thrd_Sleep(1000);
! 	}
  }
  
  long smb_ReceiveCoreGetDiskAttributes(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
! 	osi_Log0(smb_logp, "SMB receive get disk attributes");
  
! 	smb_SetSMBParm(outp, 0, 32000);
! 	smb_SetSMBParm(outp, 1, 64);
! 	smb_SetSMBParm(outp, 2, 1024);
! 	smb_SetSMBParm(outp, 3, 30000);
! 	smb_SetSMBParm(outp, 4, 0);
! 	smb_SetSMBDataLength(outp, 0);
! 	return 0;
  }
  
  long smb_ReceiveCoreTreeConnect(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *rsp)
  {
! 	smb_tid_t *tidp;
      smb_user_t *uidp;
! 	unsigned short newTid;
! 	char shareName[256];
! 	char *sharePath;
! 	int shareFound;
! 	char *tp;
! 	char *pathp;
! 	char *passwordp;
! 	cm_user_t *userp;
! 
! 	osi_Log0(smb_logp, "SMB receive tree connect");
! 
! 	/* parse input parameters */
! 	tp = smb_GetSMBData(inp, NULL);
! 	pathp = smb_ParseASCIIBlock(tp, &tp);
! 	passwordp = smb_ParseASCIIBlock(tp, &tp);
! 	tp = strrchr(pathp, '\\');
! 	if (!tp)
! 		return CM_ERROR_BADSMB;
! 	strcpy(shareName, tp+1);
! 
! 	userp = smb_GetUser(vcp, inp);
! 
! 	lock_ObtainMutex(&vcp->mx);
! 	newTid = vcp->tidCounter++;
! 	lock_ReleaseMutex(&vcp->mx);
!         
! 	tidp = smb_FindTID(vcp, newTid, SMB_FLAG_CREATE);
      uidp = smb_FindUID(vcp, ((smb_t *)inp)->uid, 0);
! 	shareFound = smb_FindShare(vcp, uidp, shareName, &sharePath);
      if (uidp)
          smb_ReleaseUID(uidp);
! 	if (!shareFound) {
! 		smb_ReleaseTID(tidp);
! 		return CM_ERROR_BADSHARENAME;
! 	}
! 	lock_ObtainMutex(&tidp->mx);
! 	tidp->userp = userp;
! 	tidp->pathname = sharePath;
! 	lock_ReleaseMutex(&tidp->mx);
! 	smb_ReleaseTID(tidp);
  
! 	smb_SetSMBParm(rsp, 0, SMB_PACKETSIZE);
! 	smb_SetSMBParm(rsp, 1, newTid);
! 	smb_SetSMBDataLength(rsp, 0);
! 
! 	osi_Log1(smb_logp, "SMB tree connect created ID %d", newTid);
! 	return 0;
  }
  
  unsigned char *smb_ParseDataBlock(unsigned char *inp, char **chainpp, int *lengthp)
  {
! 	int tlen;
  
! 	if (*inp++ != 0x1) return NULL;
! 	tlen = inp[0] + (inp[1]<<8);
! 	inp += 2;		/* skip length field */
          
! 	if (chainpp) {
! 		*chainpp = inp + tlen;
! 	}	
!         
! 	if (lengthp) *lengthp = tlen;
          
! 	return inp;
  }
  
  /* set maskp to the mask part of the incoming path.
--- 2862,3020 ----
                  cm_noteLocalMountPointChange();
  #endif
          }
!         /* XXX GC dir search entries */
!     }
  }
  
  void smb_WaitingLocksDaemon()
  {
!     smb_waitingLock_t *wL, *nwL;
!     int first;
!     smb_vc_t *vcp;
!     smb_packet_t *inp, *outp;
!     NCB *ncbp;
!     long code = 0;
! 
!     while (1) {
!         lock_ObtainWrite(&smb_globalLock);
!         nwL = smb_allWaitingLocks;
!         if (nwL == NULL) {
!             osi_SleepW((long)&smb_allWaitingLocks, &smb_globalLock);
!             thrd_Sleep(1000);
!             continue;
!         }
!         else first = 1;
!         do {
!             if (first)
!                 first = 0;
!             else
!                 lock_ObtainWrite(&smb_globalLock);
!             wL = nwL;
!             nwL = (smb_waitingLock_t *) osi_QNext(&wL->q);
!             lock_ReleaseWrite(&smb_globalLock);
!             code = cm_RetryLock((cm_file_lock_t *) wL->lockp,
!                                  wL->vcp->flags & SMB_VCFLAG_ALREADYDEAD);
!             if (code == CM_ERROR_WOULDBLOCK) {
!                 /* no progress */
!                 if (wL->timeRemaining != 0xffffffff
!                      && (wL->timeRemaining -= 1000) < 0)
!                     goto endWait;
!                 continue;
!             }
!           endWait:
!             vcp = wL->vcp;
!             inp = wL->inp;
!             outp = wL->outp;
!             ncbp = GetNCB();
!             ncbp->ncb_length = inp->ncb_length;
!             inp->spacep = cm_GetSpace();
! 
!             /* Remove waitingLock from list */
!             lock_ObtainWrite(&smb_globalLock);
!             osi_QRemove((osi_queue_t **)&smb_allWaitingLocks,
!                          &wL->q);
!             lock_ReleaseWrite(&smb_globalLock);
! 
!             /* Resume packet processing */
!             if (code == 0)
!                 smb_SetSMBDataLength(outp, 0);
!             outp->flags |= SMB_PACKETFLAG_SUSPENDED;
!             outp->resumeCode = code;
!             outp->ncbp = ncbp;
!             smb_DispatchPacket(vcp, inp, outp, ncbp, NULL);
! 
!             /* Clean up */
!             cm_FreeSpace(inp->spacep);
!             smb_FreePacket(inp);
!             smb_FreePacket(outp);
!             FreeNCB(ncbp);
!             free(wL);
!         } while (nwL);
!         thrd_Sleep(1000);
!     }
  }
  
  long smb_ReceiveCoreGetDiskAttributes(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
!     osi_Log0(smb_logp, "SMB receive get disk attributes");
  
!     smb_SetSMBParm(outp, 0, 32000);
!     smb_SetSMBParm(outp, 1, 64);
!     smb_SetSMBParm(outp, 2, 1024);
!     smb_SetSMBParm(outp, 3, 30000);
!     smb_SetSMBParm(outp, 4, 0);
!     smb_SetSMBDataLength(outp, 0);
!     return 0;
  }
  
  long smb_ReceiveCoreTreeConnect(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *rsp)
  {
!     smb_tid_t *tidp;
      smb_user_t *uidp;
!     unsigned short newTid;
!     char shareName[256];
!     char *sharePath;
!     int shareFound;
!     char *tp;
!     char *pathp;
!     char *passwordp;
!     cm_user_t *userp;
! 
!     osi_Log0(smb_logp, "SMB receive tree connect");
! 
!     /* parse input parameters */
!     tp = smb_GetSMBData(inp, NULL);
!     pathp = smb_ParseASCIIBlock(tp, &tp);
!     passwordp = smb_ParseASCIIBlock(tp, &tp);
!     tp = strrchr(pathp, '\\');
!     if (!tp)
!         return CM_ERROR_BADSMB;
!     strcpy(shareName, tp+1);
! 
!     userp = smb_GetUser(vcp, inp);
! 
!     lock_ObtainMutex(&vcp->mx);
!     newTid = vcp->tidCounter++;
!     lock_ReleaseMutex(&vcp->mx);
! 
!     tidp = smb_FindTID(vcp, newTid, SMB_FLAG_CREATE);
      uidp = smb_FindUID(vcp, ((smb_t *)inp)->uid, 0);
!     shareFound = smb_FindShare(vcp, uidp, shareName, &sharePath);
      if (uidp)
          smb_ReleaseUID(uidp);
!     if (!shareFound) {
!         smb_ReleaseTID(tidp);
!         return CM_ERROR_BADSHARENAME;
!     }
!     lock_ObtainMutex(&tidp->mx);
!     tidp->userp = userp;
!     tidp->pathname = sharePath;
!     lock_ReleaseMutex(&tidp->mx);
!     smb_ReleaseTID(tidp);
! 
!     smb_SetSMBParm(rsp, 0, SMB_PACKETSIZE);
!     smb_SetSMBParm(rsp, 1, newTid);
!     smb_SetSMBDataLength(rsp, 0);
  
!     osi_Log1(smb_logp, "SMB tree connect created ID %d", newTid);
!     return 0;
  }
  
  unsigned char *smb_ParseDataBlock(unsigned char *inp, char **chainpp, int *lengthp)
  {
!     int tlen;
  
!     if (*inp++ != 0x1) return NULL;
!     tlen = inp[0] + (inp[1]<<8);
!     inp += 2;		/* skip length field */
          
!     if (chainpp) {
!         *chainpp = inp + tlen;
!     }	
! 
!     if (lengthp) *lengthp = tlen;
          
!     return inp;
  }
  
  /* set maskp to the mask part of the incoming path.
***************
*** 2994,3023 ****
   */
  int smb_Get8Dot3MaskFromPath(unsigned char *maskp, unsigned char *pathp)
  {
! 	char *tp;
! 	char *up;
! 	int i;
! 	int tc;
! 	int valid8Dot3;
! 
! 	/* starts off valid */
! 	valid8Dot3 = 1;
! 
! 	/* mask starts out all blanks */
! 	memset(maskp, ' ', 11);
! 
! 	/* find last backslash, or use whole thing if there is none */
! 	tp = strrchr(pathp, '\\');
! 	if (!tp) tp = pathp;
! 	else tp++;	/* skip slash */
          
! 	up = maskp;
  
! 	/* names starting with a dot are illegal */
! 	if (*tp == '.') valid8Dot3 = 0;
  
      for(i=0;; i++) {
! 		tc = *tp++;
          if (tc == 0) return valid8Dot3;
          if (tc == '.' || tc == '"') break;
          if (i < 8) *up++ = tc;
--- 3024,3053 ----
   */
  int smb_Get8Dot3MaskFromPath(unsigned char *maskp, unsigned char *pathp)
  {
!     char *tp;
!     char *up;
!     int i;
!     int tc;
!     int valid8Dot3;
! 
!     /* starts off valid */
!     valid8Dot3 = 1;
! 
!     /* mask starts out all blanks */
!     memset(maskp, ' ', 11);
! 
!     /* find last backslash, or use whole thing if there is none */
!     tp = strrchr(pathp, '\\');
!     if (!tp) tp = pathp;
!     else tp++;	/* skip slash */
          
!     up = maskp;
  
!     /* names starting with a dot are illegal */
!     if (*tp == '.') valid8Dot3 = 0;
  
      for(i=0;; i++) {
!         tc = *tp++;
          if (tc == 0) return valid8Dot3;
          if (tc == '.' || tc == '"') break;
          if (i < 8) *up++ = tc;
***************
*** 3029,3045 ****
      for(i=0;;i++) {
          tc = *tp++;
          if (tc == 0) 
! 			return valid8Dot3;
  
          /* too many dots */
          if (tc == '.' || tc == '"') 
! 			valid8Dot3 = 0;
  
          /* copy extension if not too long */
          if (i < 3) 
! 			*up++ = tc;
          else 
! 			valid8Dot3 = 0;
      }   
  
      /* unreachable */
--- 3059,3075 ----
      for(i=0;;i++) {
          tc = *tp++;
          if (tc == 0) 
!             return valid8Dot3;
  
          /* too many dots */
          if (tc == '.' || tc == '"') 
!             valid8Dot3 = 0;
  
          /* copy extension if not too long */
          if (i < 3) 
!             *up++ = tc;
          else 
!             valid8Dot3 = 0;
      }   
  
      /* unreachable */
***************
*** 3047,3183 ****
  
  int smb_Match8Dot3Mask(char *unixNamep, char *maskp)
  {
! 	char umask[11];
! 	int valid;
! 	int i;
! 	char tc1;
! 	char tc2;
! 	char *tp1;
! 	char *tp2;
! 
! 	/* XXX redo this, calling smb_V3MatchMask with a converted mask */
! 
! 	valid = smb_Get8Dot3MaskFromPath(umask, unixNamep);
! 	if (!valid) 
! 		return 0;
   
! 	/* otherwise, we have a valid 8.3 name; see if we have a match,
! 	 * treating '?' as a wildcard in maskp (but not in the file name).
! 	 */
! 	tp1 = umask;	/* real name, in mask format */
! 	tp2 = maskp;	/* mask, in mask format */
! 	for(i=0; i<11; i++) {
! 		tc1 = *tp1++;	/* char from real name */
! 		tc2 = *tp2++;	/* char from mask */
! 		tc1 = (char) cm_foldUpper[(unsigned char)tc1];
! 		tc2 = (char) cm_foldUpper[(unsigned char)tc2];
! 		if (tc1 == tc2) 
! 			continue;
! 		if (tc2 == '?' && tc1 != ' ') 
! 			continue;
! 		if (tc2 == '>') 
! 			continue;
! 		return 0;
! 	}
  
! 	/* we got a match */
! 	return 1;
  }
  
  char *smb_FindMask(char *pathp)
  {
! 	char *tp;
          
! 	tp = strrchr(pathp, '\\');	/* find last slash */
  
! 	if (tp) 
! 		return tp+1;	/* skip the slash */
! 	else 
! 		return pathp;	/* no slash, return the entire path */
! }
  
  long smb_ReceiveCoreSearchVolume(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
! 	unsigned char *pathp;
! 	unsigned char *tp;
! 	unsigned char mask[11];
! 	unsigned char *statBlockp;
! 	unsigned char initStatBlock[21];
! 	int statLen;
!         
! 	osi_Log0(smb_logp, "SMB receive search volume");
! 
! 	/* pull pathname and stat block out of request */
! 	tp = smb_GetSMBData(inp, NULL);
! 	pathp = smb_ParseASCIIBlock(tp, (char **) &tp);
! 	osi_assert(pathp != NULL);
! 	statBlockp = smb_ParseVblBlock(tp, (char **) &tp, &statLen);
! 	osi_assert(statBlockp != NULL);
! 	if (statLen == 0) {
! 		statBlockp = initStatBlock;
! 		statBlockp[0] = 8;
! 	}
!         
! 	/* for returning to caller */
! 	smb_Get8Dot3MaskFromPath(mask, pathp);
!         
! 	smb_SetSMBParm(outp, 0, 1);		/* we're returning one entry */
! 	tp = smb_GetSMBData(outp, NULL);
! 	*tp++ = 5;
! 	*tp++ = 43;	/* bytes in a dir entry */
! 	*tp++ = 0;	/* high byte in counter */
! 
! 	/* now marshall the dir entry, starting with the search status */
! 	*tp++ = statBlockp[0];		/* Reserved */
! 	memcpy(tp, mask, 11); tp += 11;	/* FileName */
! 
! 	/* now pass back server use info, with 1st byte non-zero */
! 	*tp++ = 1;
! 	memset(tp, 0, 4); tp += 4;	/* reserved for server use */
! 
! 	memcpy(tp, statBlockp+17, 4); tp += 4;	/* reserved for consumer */
! 
! 	*tp++ = 0x8;		/* attribute: volume */
! 
! 	/* copy out time */
! 	*tp++ = 0;
! 	*tp++ = 0;
! 
! 	/* copy out date */
! 	*tp++ = 18;
! 	*tp++ = 178;
! 
! 	/* 4 byte file size */
! 	*tp++ = 0;
! 	*tp++ = 0;
! 	*tp++ = 0;
! 	*tp++ = 0;
! 
! 	/* finally, null-terminated 8.3 pathname, which we set to AFS */
! 	memset(tp, ' ', 13);
! 	strcpy(tp, "AFS");
! 
! 	/* set the length of the data part of the packet to 43 + 3, for the dir
! 	 * entry plus the 5 and the length fields.
! 	 */
! 	smb_SetSMBDataLength(outp, 46);
! 	return 0;
! }
  
  long smb_ApplyDirListPatches(smb_dirListPatch_t **dirPatchespp,
! 	cm_user_t *userp, cm_req_t *reqp)
  {
! 	long code = 0;
! 	cm_scache_t *scp;
! 	char *dptr;
! 	time_t dosTime;
! 	u_short shortTemp;
! 	char attr;
! 	smb_dirListPatch_t *patchp;
! 	smb_dirListPatch_t *npatchp;
  
! 	for(patchp = *dirPatchespp; patchp; patchp =
! 		 (smb_dirListPatch_t *) osi_QNext(&patchp->q)) {
  
          dptr = patchp->dptr;
  
--- 3077,3213 ----
  
  int smb_Match8Dot3Mask(char *unixNamep, char *maskp)
  {
!     char umask[11];
!     int valid;
!     int i;
!     char tc1;
!     char tc2;
!     char *tp1;
!     char *tp2;
! 
!     /* XXX redo this, calling smb_V3MatchMask with a converted mask */
! 
!     valid = smb_Get8Dot3MaskFromPath(umask, unixNamep);
!     if (!valid) 
!         return 0;
   
!     /* otherwise, we have a valid 8.3 name; see if we have a match,
!      * treating '?' as a wildcard in maskp (but not in the file name).
!      */
!     tp1 = umask;	/* real name, in mask format */
!     tp2 = maskp;	/* mask, in mask format */
!     for(i=0; i<11; i++) {
!         tc1 = *tp1++;	/* char from real name */
!         tc2 = *tp2++;	/* char from mask */
!         tc1 = (char) cm_foldUpper[(unsigned char)tc1];
!         tc2 = (char) cm_foldUpper[(unsigned char)tc2];
!         if (tc1 == tc2) 
!             continue;
!         if (tc2 == '?' && tc1 != ' ') 
!             continue;
!         if (tc2 == '>') 
!             continue;
!         return 0;
!     }
  
!     /* we got a match */
!     return 1;
  }
  
  char *smb_FindMask(char *pathp)
  {
!     char *tp;
          
!     tp = strrchr(pathp, '\\');	/* find last slash */
  
!     if (tp) 
!         return tp+1;	/* skip the slash */
!     else 
!         return pathp;	/* no slash, return the entire path */
! }       
  
  long smb_ReceiveCoreSearchVolume(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
!     unsigned char *pathp;
!     unsigned char *tp;
!     unsigned char mask[11];
!     unsigned char *statBlockp;
!     unsigned char initStatBlock[21];
!     int statLen;
!         
!     osi_Log0(smb_logp, "SMB receive search volume");
! 
!     /* pull pathname and stat block out of request */
!     tp = smb_GetSMBData(inp, NULL);
!     pathp = smb_ParseASCIIBlock(tp, (char **) &tp);
!     osi_assert(pathp != NULL);
!     statBlockp = smb_ParseVblBlock(tp, (char **) &tp, &statLen);
!     osi_assert(statBlockp != NULL);
!     if (statLen == 0) {
!         statBlockp = initStatBlock;
!         statBlockp[0] = 8;
!     }
!         
!     /* for returning to caller */
!     smb_Get8Dot3MaskFromPath(mask, pathp);
! 
!     smb_SetSMBParm(outp, 0, 1);		/* we're returning one entry */
!     tp = smb_GetSMBData(outp, NULL);
!     *tp++ = 5;
!     *tp++ = 43;	/* bytes in a dir entry */
!     *tp++ = 0;	/* high byte in counter */
! 
!     /* now marshall the dir entry, starting with the search status */
!     *tp++ = statBlockp[0];		/* Reserved */
!     memcpy(tp, mask, 11); tp += 11;	/* FileName */
! 
!     /* now pass back server use info, with 1st byte non-zero */
!     *tp++ = 1;
!     memset(tp, 0, 4); tp += 4;	/* reserved for server use */
! 
!     memcpy(tp, statBlockp+17, 4); tp += 4;	/* reserved for consumer */
! 
!     *tp++ = 0x8;		/* attribute: volume */
! 
!     /* copy out time */
!     *tp++ = 0;
!     *tp++ = 0;
! 
!     /* copy out date */
!     *tp++ = 18;
!     *tp++ = 178;
! 
!     /* 4 byte file size */
!     *tp++ = 0;
!     *tp++ = 0;
!     *tp++ = 0;
!     *tp++ = 0;
! 
!     /* finally, null-terminated 8.3 pathname, which we set to AFS */
!     memset(tp, ' ', 13);
!     strcpy(tp, "AFS");
! 
!     /* set the length of the data part of the packet to 43 + 3, for the dir
!      * entry plus the 5 and the length fields.
!      */
!     smb_SetSMBDataLength(outp, 46);
!     return 0;
! }       
  
  long smb_ApplyDirListPatches(smb_dirListPatch_t **dirPatchespp,
!                              cm_user_t *userp, cm_req_t *reqp)
  {
!     long code = 0;
!     cm_scache_t *scp;
!     char *dptr;
!     time_t dosTime;
!     u_short shortTemp;
!     char attr;
!     smb_dirListPatch_t *patchp;
!     smb_dirListPatch_t *npatchp;
  
!     for (patchp = *dirPatchespp; patchp; patchp =
!          (smb_dirListPatch_t *) osi_QNext(&patchp->q)) {
  
          dptr = patchp->dptr;
  
***************
*** 3187,4075 ****
                  *dptr++ = SMB_ATTR_HIDDEN;
              continue;
          }
! 		lock_ObtainMutex(&scp->mx);
! 		code = cm_SyncOp(scp, NULL, userp, reqp, 0,
! 						  CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
! 		if (code) {	
! 			lock_ReleaseMutex(&scp->mx);
! 			cm_ReleaseSCache(scp);
!             if( patchp->flags & SMB_DIRLISTPATCH_DOTFILE )
                  *dptr++ = SMB_ATTR_HIDDEN;
! 			continue;
! 		}
  
! 		attr = smb_Attributes(scp);
          /* check hidden attribute (the flag is only ON when dot file hiding is on ) */
!         if( patchp->flags & SMB_DIRLISTPATCH_DOTFILE )
              attr |= SMB_ATTR_HIDDEN;
          *dptr++ = attr;
  
          /* get dos time */
! 		smb_SearchTimeFromUnixTime(&dosTime, scp->clientModTime);
                  
! 		/* copy out time */
! 		shortTemp = dosTime & 0xffff;
! 		*((u_short *)dptr) = shortTemp;
! 		dptr += 2;
! 
! 		/* and copy out date */
! 		shortTemp = (dosTime>>16) & 0xffff;
! 		*((u_short *)dptr) = shortTemp;
! 		dptr += 2;
                  
! 		/* copy out file length */
! 		*((u_long *)dptr) = scp->length.LowPart;
! 		dptr += 4;
! 		lock_ReleaseMutex(&scp->mx);
! 		cm_ReleaseSCache(scp);
! 	}
!         
! 	/* now free the patches */
! 	for(patchp = *dirPatchespp; patchp; patchp = npatchp) {
! 		npatchp = (smb_dirListPatch_t *) osi_QNext(&patchp->q);
! 		free(patchp);
! 	}	
          
! 	/* and mark the list as empty */
! 	*dirPatchespp = NULL;
  
! 	return code;
  }
  
  long smb_ReceiveCoreSearchDir(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
! 	int attribute;
! 	long nextCookie;
! 	char *tp;
! 	long code = 0;
! 	char *pathp;
! 	cm_dirEntry_t *dep;
! 	int maxCount;
! 	smb_dirListPatch_t *dirListPatchesp;
! 	smb_dirListPatch_t *curPatchp;
! 	int dataLength;
! 	cm_buf_t *bufferp;
! 	long temp;
! 	osi_hyper_t dirLength;
! 	osi_hyper_t bufferOffset;
! 	osi_hyper_t curOffset;
! 	osi_hyper_t thyper;
! 	unsigned char *inCookiep;
! 	smb_dirSearch_t *dsp;
! 	cm_scache_t *scp;
! 	long entryInDir;
! 	long entryInBuffer;
! 	unsigned long clientCookie;
! 	cm_pageHeader_t *pageHeaderp;
! 	cm_user_t *userp = NULL;
! 	int slotInPage;
! 	char shortName[13];
! 	char *actualName;
! 	char *shortNameEnd;
! 	char mask[11];
! 	int returnedNames;
! 	long nextEntryCookie;
! 	int numDirChunks;		/* # of 32 byte dir chunks in this entry */
! 	char resByte;			/* reserved byte from the cookie */
! 	char *op;			/* output data ptr */
! 	char *origOp;			/* original value of op */
! 	cm_space_t *spacep;		/* for pathname buffer */
! 	int starPattern;
! 	int rootPath = 0;
! 	int caseFold;
! 	char *tidPathp;
! 	cm_req_t req;
! 	cm_fid_t fid;
! 	int fileType;
! 
! 	cm_InitReq(&req);
! 
! 	maxCount = smb_GetSMBParm(inp, 0);
! 
! 	dirListPatchesp = NULL;
!         
! 	caseFold = CM_FLAG_CASEFOLD;
! 
! 	tp = smb_GetSMBData(inp, NULL);
! 	pathp = smb_ParseASCIIBlock(tp, &tp);
! 	inCookiep = smb_ParseVblBlock(tp, &tp, &dataLength);
! 
! 	/* bail out if request looks bad */
! 	if (!tp || !pathp) {
! 		return CM_ERROR_BADSMB;
! 	}
! 
! 	/* We can handle long names */
! 	if (vcp->flags & SMB_VCFLAG_USENT)
! 		((smb_t *)outp)->flg2 |= 0x40;	/* IS_LONG_NAME */
! 
! 	/* make sure we got a whole search status */
! 	if (dataLength < 21) {
! 		nextCookie = 0;		/* start at the beginning of the dir */
! 		resByte = 0;
! 		clientCookie = 0;
! 		attribute = smb_GetSMBParm(inp, 1);
! 
! 		/* handle volume info in another function */
! 		if (attribute & 0x8)
! 			return smb_ReceiveCoreSearchVolume(vcp, inp, outp);
! 
! 		osi_Log2(smb_logp, "SMB receive search dir count %d [%s]",
! 				 maxCount, osi_LogSaveString(smb_logp, pathp));
! 
! 		if (*pathp == 0) {	/* null pathp, treat as root dir */
! 			if (!(attribute & SMB_ATTR_DIRECTORY))	/* exclude dirs */
! 				return CM_ERROR_NOFILES;
! 			rootPath = 1;
! 		}
! 
! 		dsp = smb_NewDirSearch(0);
! 		dsp->attribute = attribute;
! 		smb_Get8Dot3MaskFromPath(mask, pathp);
! 		memcpy(dsp->mask, mask, 11);
! 
! 		/* track if this is likely to match a lot of entries */
! 		if (smb_IsStarMask(mask)) starPattern = 1;
! 		else starPattern = 0;
! 	}	
! 	else {
! 		/* pull the next cookie value out of the search status block */
! 		nextCookie = inCookiep[13] + (inCookiep[14]<<8) + (inCookiep[15]<<16)
! 			+ (inCookiep[16]<<24);
! 		dsp = smb_FindDirSearch(inCookiep[12]);
! 		if (!dsp) {
! 			/* can't find dir search status; fatal error */
! 			return CM_ERROR_BADFD;
! 		}
! 		attribute = dsp->attribute;
! 		resByte = inCookiep[0];
! 
! 		/* copy out client cookie, in host byte order.  Don't bother
! 		 * interpreting it, since we're just passing it through, anyway.
! 		 */
! 		memcpy(&clientCookie, &inCookiep[17], 4);
! 
! 		memcpy(mask, dsp->mask, 11);
! 
! 		/* assume we're doing a star match if it has continued for more
! 		 * than one call.
! 		 */
! 		starPattern = 1;
! 	}
! 
! 	osi_Log3(smb_logp, "SMB dir search cookie 0x%x, connection %d, attr 0x%x",
! 			 nextCookie, dsp->cookie, attribute);
! 
! 	userp = smb_GetUser(vcp, inp);
! 
! 	/* try to get the vnode for the path name next */
! 	lock_ObtainMutex(&dsp->mx);
! 	if (dsp->scp) {
! 		scp = dsp->scp;
! 		cm_HoldSCache(scp);
! 		code = 0;
! 	}
! 	else {
! 		spacep = inp->spacep;
! 		smb_StripLastComponent(spacep->data, NULL, pathp);
! 		lock_ReleaseMutex(&dsp->mx);
! 		code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!         if(code) {
!             lock_ReleaseMutex(&dsp->mx);
!             cm_ReleaseUser(userp);
!             smb_DeleteDirSearch(dsp);
!             smb_ReleaseDirSearch(dsp);
!             return CM_ERROR_NOFILES;
!         }
! 		code = cm_NameI(cm_rootSCachep, spacep->data,
! 						caseFold | CM_FLAG_FOLLOW, userp, tidPathp, &req, &scp);
! 		lock_ObtainMutex(&dsp->mx);
! 		if (code == 0) {
! 			if (dsp->scp != 0) cm_ReleaseSCache(dsp->scp);
! 			dsp->scp = scp;
! 			/* we need one hold for the entry we just stored into,
! 			 * and one for our own processing.  When we're done with this
! 			 * function, we'll drop the one for our own processing.
! 			 * We held it once from the namei call, and so we do another hold
! 			 * now.
! 			 */
! 			cm_HoldSCache(scp);
!    			lock_ObtainMutex(&scp->mx);
!    			if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
!    			    && LargeIntegerGreaterOrEqualToZero(scp->bulkStatProgress)) {
!    				scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
!    				dsp->flags |= SMB_DIRSEARCH_BULKST;
! 			}
! 			lock_ReleaseMutex(&scp->mx);
! 		}
! 	}
! 	lock_ReleaseMutex(&dsp->mx);
! 	if (code) {
! 		cm_ReleaseUser(userp);
! 		smb_DeleteDirSearch(dsp);
! 		smb_ReleaseDirSearch(dsp);
! 		return code;
! 	}
! 
! 	/* reserves space for parameter; we'll adjust it again later to the
! 	 * real count of the # of entries we returned once we've actually
! 	 * assembled the directory listing.
! 	 */
! 	smb_SetSMBParm(outp, 0, 0);
! 	
! 	/* get the directory size */
! 	lock_ObtainMutex(&scp->mx);
! 	code = cm_SyncOp(scp, NULL, userp, &req, 0,
! 					 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
! 	if (code) {
! 		lock_ReleaseMutex(&scp->mx);
! 		cm_ReleaseSCache(scp);
! 		cm_ReleaseUser(userp);
! 		smb_DeleteDirSearch(dsp);
! 		smb_ReleaseDirSearch(dsp);
! 		return code;
! 	}
!         
! 	dirLength = scp->length;
! 	bufferp = NULL;
! 	bufferOffset.LowPart = bufferOffset.HighPart = 0;
! 	curOffset.HighPart = 0;
! 	curOffset.LowPart = nextCookie;
! 	origOp = op = smb_GetSMBData(outp, NULL);
! 	/* and write out the basic header */
! 	*op++ = 5;		/* variable block */
! 	op += 2;		/* skip vbl block length; we'll fill it in later */
! 	code = 0;
! 	returnedNames = 0;
! 	while (1) {
! 		/* make sure that curOffset.LowPart doesn't point to the first
! 		 * 32 bytes in the 2nd through last dir page, and that it doesn't
! 		 * point at the first 13 32-byte chunks in the first dir page,
! 		 * since those are dir and page headers, and don't contain useful
! 		 * information.
! 		 */
! 		temp = curOffset.LowPart & (2048-1);
! 		if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
! 			/* we're in the first page */
! 			if (temp < 13*32) temp = 13*32;
! 		}
! 		else {
! 			/* we're in a later dir page */
! 			if (temp < 32) temp = 32;
! 		}
! 		
! 		/* make sure the low order 5 bits are zero */
! 		temp &= ~(32-1);
! 
! 		/* now put temp bits back ito curOffset.LowPart */
! 		curOffset.LowPart &= ~(2048-1);
! 		curOffset.LowPart |= temp;
! 
! 		/* check if we've returned all the names that will fit in the
! 		 * response packet.
! 		 */
! 		if (returnedNames >= maxCount) 
! 			break;
!                 
! 		/* check if we've passed the dir's EOF */
! 		if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength)) break;
! 
! 		/* see if we can use the bufferp we have now; compute in which page
! 		 * the current offset would be, and check whether that's the offset
! 		 * of the buffer we have.  If not, get the buffer.
! 		 */
! 		thyper.HighPart = curOffset.HighPart;
! 		thyper.LowPart = curOffset.LowPart & ~(buf_bufferSize-1);
! 		if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
! 			/* wrong buffer */
! 			if (bufferp) {
! 				buf_Release(bufferp);
! 				bufferp = NULL;
! 			}	
! 			lock_ReleaseMutex(&scp->mx);
! 			lock_ObtainRead(&scp->bufCreateLock);
! 			code = buf_Get(scp, &thyper, &bufferp);
! 			lock_ReleaseRead(&scp->bufCreateLock);
! 
! 			/* now, if we're doing a star match, do bulk fetching of all of 
! 			 * the status info for files in the dir.
! 			 */
! 			if (starPattern) {
! 				smb_ApplyDirListPatches(&dirListPatchesp, userp,
! 										&req);
! 				if ((dsp->flags & SMB_DIRSEARCH_BULKST)
!    				    && LargeIntegerGreaterThanOrEqualTo(thyper, 
! 									scp->bulkStatProgress)) {
! 					/* Don't bulk stat if risking timeout */
! 					int now = GetCurrentTime();
! 					if (now - req.startTime > 5000) {
! 						scp->bulkStatProgress = thyper;
! 						scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
! 						dsp->flags &= ~SMB_DIRSEARCH_BULKST;
! 					} else
! 						cm_TryBulkStat(scp, &thyper, userp, &req);
!    				}
! 			}
! 
! 			lock_ObtainMutex(&scp->mx);
! 			if (code) 
! 				break;
! 			bufferOffset = thyper;
! 
! 			/* now get the data in the cache */
! 			while (1) {
! 				code = cm_SyncOp(scp, bufferp, userp, &req,
! 								  PRSFS_LOOKUP,
! 								  CM_SCACHESYNC_NEEDCALLBACK
! 								  | CM_SCACHESYNC_READ);
! 				if (code) break;
!                                 
! 				if (cm_HaveBuffer(scp, bufferp, 0)) break;
  
! 				/* otherwise, load the buffer and try again */
! 				code = cm_GetBuffer(scp, bufferp, NULL, userp,
! 									&req);
! 				if (code) break;
! 			}
! 			if (code) {
! 				buf_Release(bufferp);
! 				bufferp = NULL;
! 				break;
! 			}
! 		}	/* if (wrong buffer) ... */
! 
! 		/* now we have the buffer containing the entry we're interested in; copy
! 		 * it out if it represents a non-deleted entry.
! 		 */
! 		entryInDir = curOffset.LowPart & (2048-1);
! 		entryInBuffer = curOffset.LowPart & (buf_bufferSize - 1);
! 
! 		/* page header will help tell us which entries are free.  Page header
! 		 * can change more often than once per buffer, since AFS 3 dir page size
! 		 * may be less than (but not more than a buffer package buffer.
! 		 */
! 		temp = curOffset.LowPart & (buf_bufferSize - 1);  /* only look intra-buffer */
! 		temp &= ~(2048 - 1);	/* turn off intra-page bits */
! 		pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
! 
! 		/* now determine which entry we're looking at in the page.  If it is
! 		 * free (there's a free bitmap at the start of the dir), we should
! 		 * skip these 32 bytes.
! 		 */
! 		slotInPage = (entryInDir & 0x7e0) >> 5;
! 		if (!(pageHeaderp->freeBitmap[slotInPage>>3] & (1 << (slotInPage & 0x7)))) {
! 			/* this entry is free */
! 			numDirChunks = 1;		/* only skip this guy */
! 			goto nextEntry;
! 		}
! 
! 		tp = bufferp->datap + entryInBuffer;
! 		dep = (cm_dirEntry_t *) tp;		/* now points to AFS3 dir entry */
! 
! 		/* while we're here, compute the next entry's location, too,
! 		 * since we'll need it when writing out the cookie into the dir
! 		 * listing stream.
! 		 *
! 		 * XXXX Probably should do more sanity checking.
! 		 */
! 		numDirChunks = cm_NameEntries(dep->name, NULL);
! 		
! 		/* compute the offset of the cookie representing the next entry */
! 		nextEntryCookie = curOffset.LowPart + (CM_DIR_CHUNKSIZE * numDirChunks);
  
! 		/* Compute 8.3 name if necessary */
! 		actualName = dep->name;
! 		if (dep->fid.vnode != 0 && !cm_Is8Dot3(actualName)) {
! 			cm_Gen8Dot3Name(dep, shortName, &shortNameEnd);
! 			actualName = shortName;
! 		}
! 
! 		if (dep->fid.vnode != 0 && smb_Match8Dot3Mask(actualName, mask)) {
! 			/* this is one of the entries to use: it is not deleted
! 			 * and it matches the star pattern we're looking for.
! 			 */
! 
! 			/* Eliminate entries that don't match requested
! 			   attributes */
! 
! 			/* no hidden files */
! 			if(smb_hideDotFiles && !(dsp->attribute & SMB_ATTR_HIDDEN) && smb_IsDotFile(actualName))
! 				goto nextEntry;
! 
! 			if (!(dsp->attribute & SMB_ATTR_DIRECTORY))  /* no directories */
! 			{
! 				/* We have already done the cm_TryBulkStat above */
! 				fid.cell = scp->fid.cell;
! 				fid.volume = scp->fid.volume;
! 				fid.vnode = ntohl(dep->fid.vnode);
! 				fid.unique = ntohl(dep->fid.unique);
! 				fileType = cm_FindFileType(&fid);
! 				osi_Log2(smb_logp, "smb_ReceiveCoreSearchDir: file %s "
!                                                   "has filetype %d", osi_LogSaveString(smb_logp, dep->name),
! 						  fileType);
! 				if (fileType == CM_SCACHETYPE_DIRECTORY)
! 					goto nextEntry;
! 			}
! 
! 			*op++ = resByte;
! 			memcpy(op, mask, 11); op += 11;
! 			*op++ = (char) dsp->cookie;	/* they say it must be non-zero */
! 			*op++ = nextEntryCookie & 0xff;
! 			*op++ = (nextEntryCookie>>8) & 0xff;
! 			*op++ = (nextEntryCookie>>16) & 0xff;
! 			*op++ = (nextEntryCookie>>24) & 0xff;
! 			memcpy(op, &clientCookie, 4); op += 4;
! 
! 			/* now we emit the attribute.  This is sort of tricky,
! 			 * since we need to really stat the file to find out
! 			 * what type of entry we've got.  Right now, we're
! 			 * copying out data from a buffer, while holding the
! 			 * scp locked, so it isn't really convenient to stat
! 			 * something now.  We'll put in a place holder now,
! 			 * and make a second pass before returning this to get
! 			 * the real attributes.  So, we just skip the data for
! 			 * now, and adjust it later.  We allocate a patch
! 			 * record to make it easy to find this point later.
! 			 * The replay will happen at a time when it is safe to
! 			 * unlock the directory.
! 			 */
! 			curPatchp = malloc(sizeof(*curPatchp));
! 			osi_QAdd((osi_queue_t **) &dirListPatchesp, &curPatchp->q);
! 			curPatchp->dptr = op;
! 			curPatchp->fid.cell = scp->fid.cell;
! 			curPatchp->fid.volume = scp->fid.volume;
! 			curPatchp->fid.vnode = ntohl(dep->fid.vnode);
! 			curPatchp->fid.unique = ntohl(dep->fid.unique);
! 
! 			/* do hidden attribute here since name won't be around when applying
! 			 * dir list patches
! 			 */
! 
! 			if ( smb_hideDotFiles && smb_IsDotFile(actualName) )
! 				curPatchp->flags = SMB_DIRLISTPATCH_DOTFILE;
! 			else
! 				curPatchp->flags = 0;
! 
! 			op += 9;	/* skip attr, time, date and size */
! 
! 			/* zero out name area.  The spec says to pad with
! 			 * spaces, but Samba doesn't, and neither do we.
! 			 */
! 			memset(op, 0, 13);
! 
! 			/* finally, we get to copy out the name; we know that
! 			 * it fits in 8.3 or the pattern wouldn't match, but it
! 			 * never hurts to be sure.
! 			 */
! 			strncpy(op, actualName, 13);
! 
! 			/* Uppercase if requested by client */
! 			if ((((smb_t *)inp)->flg2 & 1) == 0)
! 				_strupr(op);
! 
! 			op += 13;
! 
! 			/* now, adjust the # of entries copied */
! 			returnedNames++;
! 		}	/* if we're including this name */
!                 
! 	  nextEntry:
! 		/* and adjust curOffset to be where the new cookie is */
! 		thyper.HighPart = 0;
! 		thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
! 		curOffset = LargeIntegerAdd(thyper, curOffset);
! 	}		/* while copying data for dir listing */
! 
! 	/* release the mutex */
! 	lock_ReleaseMutex(&scp->mx);
! 	if (bufferp) buf_Release(bufferp);
! 
! 	/* apply and free last set of patches; if not doing a star match, this
! 	 * will be empty, but better safe (and freeing everything) than sorry.
! 	 */
! 	smb_ApplyDirListPatches(&dirListPatchesp, userp, &req);
! 
! 	/* special return code for unsuccessful search */
! 	if (code == 0 && dataLength < 21 && returnedNames == 0)
! 		code = CM_ERROR_NOFILES;
! 
! 	osi_Log2(smb_logp, "SMB search dir done, %d names, code %d",
! 		 returnedNames, code);
! 
! 	if (code != 0) {
! 		smb_DeleteDirSearch(dsp);
! 		smb_ReleaseDirSearch(dsp);
! 		cm_ReleaseSCache(scp);
! 		cm_ReleaseUser(userp);
! 		return code;
! 	}
! 
! 	/* finalize the output buffer */
! 	smb_SetSMBParm(outp, 0, returnedNames);
! 	temp = (long) (op - origOp);
! 	smb_SetSMBDataLength(outp, temp);
! 
! 	/* the data area is a variable block, which has a 5 (already there)
! 	 * followed by the length of the # of data bytes.  We now know this to
! 	 * be "temp," although that includes the 3 bytes of vbl block header.
! 	 * Deduct for them and fill in the length field.
! 	 */
! 	temp -= 3;		/* deduct vbl block info */
! 	osi_assert(temp == (43 * returnedNames));
! 	origOp[1] = temp & 0xff;
! 	origOp[2] = (temp>>8) & 0xff;
! 	if (returnedNames == 0) smb_DeleteDirSearch(dsp);
! 	smb_ReleaseDirSearch(dsp);
! 	cm_ReleaseSCache(scp);
! 	cm_ReleaseUser(userp);
! 	return code;
! }	
  
! /* verify that this is a valid path to a directory.  I don't know why they
!  * don't use the get file attributes call.
!  */
! long smb_ReceiveCoreCheckPath(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
! {
! 	char *pathp;
! 	long code = 0;
! 	cm_scache_t *rootScp;
! 	cm_scache_t *newScp;
! 	cm_user_t *userp;
! 	unsigned int attrs;
! 	int caseFold;
! 	char *tidPathp;
! 	cm_req_t req;
! 
! 	cm_InitReq(&req);
! 
! 	pathp = smb_GetSMBData(inp, NULL);
! 	pathp = smb_ParseASCIIBlock(pathp, NULL);
! 	osi_Log1(smb_logp, "SMB receive check path %s",
! 			  osi_LogSaveString(smb_logp, pathp));
! 
! 	if (!pathp) {
! 		return CM_ERROR_BADFD;
! 	}
!         
! 	rootScp = cm_rootSCachep;
          
! 	userp = smb_GetUser(vcp, inp);
  
! 	caseFold = CM_FLAG_CASEFOLD;
  
! 	code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!     if(code) {
!         cm_ReleaseUser(userp);
!         return CM_ERROR_NOSUCHPATH;
      }
- 	code = cm_NameI(rootScp, pathp,
- 					 caseFold | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH,
- 					 userp, tidPathp, &req, &newScp);
- 
- 	if (code) {
- 		cm_ReleaseUser(userp);
- 		return code;
- 	}
-         
- 	/* now lock the vnode with a callback; returns with newScp locked */
- 	lock_ObtainMutex(&newScp->mx);
- 	code = cm_SyncOp(newScp, NULL, userp, &req, PRSFS_LOOKUP,
- 					  CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
- 	if (code && code != CM_ERROR_NOACCESS) {
- 		lock_ReleaseMutex(&newScp->mx);
- 		cm_ReleaseSCache(newScp);
- 		cm_ReleaseUser(userp);
- 		return code;
- 	}
- 
- 	attrs = smb_Attributes(newScp);
- 
- 	if (!(attrs & 0x10))
- 		code = CM_ERROR_NOTDIR;
- 
- 	lock_ReleaseMutex(&newScp->mx);
- 
- 	cm_ReleaseSCache(newScp);
- 	cm_ReleaseUser(userp);
- 	return code;
- }	
  
! long smb_ReceiveCoreSetFileAttributes(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
! {
! 	char *pathp;
! 	long code = 0;
! 	cm_scache_t *rootScp;
! 	unsigned short attribute;
! 	cm_attr_t attr;
! 	cm_scache_t *newScp;
! 	time_t dosTime;
! 	cm_user_t *userp;
! 	int caseFold;
! 	char *tidPathp;
! 	cm_req_t req;
  
! 	cm_InitReq(&req);
  
! 	/* decode basic attributes we're passed */
! 	attribute = smb_GetSMBParm(inp, 0);
! 	dosTime = smb_GetSMBParm(inp, 1) | (smb_GetSMBParm(inp, 2) << 16);
  
! 	pathp = smb_GetSMBData(inp, NULL);
! 	pathp = smb_ParseASCIIBlock(pathp, NULL);
!         
! 	if (!pathp) {
! 		return CM_ERROR_BADSMB;
! 	}
!         
! 	osi_Log2(smb_logp, "SMB receive setfile attributes time %d, attr 0x%x",
! 			 dosTime, attribute);
  
! 	rootScp = cm_rootSCachep;
!         
! 	userp = smb_GetUser(vcp, inp);
  
! 	caseFold = CM_FLAG_CASEFOLD;
  
! 	code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!     if(code) {
!         cm_ReleaseUser(userp);
!         return CM_ERROR_NOSUCHFILE;
!     }
! 	code = cm_NameI(rootScp, pathp, caseFold | CM_FLAG_FOLLOW, userp,
! 					tidPathp, &req, &newScp);
  
! 	if (code) {
! 		cm_ReleaseUser(userp);
! 		return code;
! 	}
! 	
! 	/* now lock the vnode with a callback; returns with newScp locked; we
! 	 * need the current status to determine what the new status is, in some
! 	 * cases.
! 	 */
! 	lock_ObtainMutex(&newScp->mx);
! 	code = cm_SyncOp(newScp, NULL, userp, &req, 0,
! 					 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
! 	if (code) {
! 		lock_ReleaseMutex(&newScp->mx);
! 		cm_ReleaseSCache(newScp);
! 		cm_ReleaseUser(userp);
! 		return code;
! 	}
! 
! 	/* Check for RO volume */
! 	if (newScp->flags & CM_SCACHEFLAG_RO) {
! 		lock_ReleaseMutex(&newScp->mx);
! 		cm_ReleaseSCache(newScp);
! 		cm_ReleaseUser(userp);
! 		return CM_ERROR_READONLY;
! 	}
! 
! 	/* prepare for setattr call */
! 	attr.mask = 0;
! 	if (dosTime != 0) {
! 		attr.mask |= CM_ATTRMASK_CLIENTMODTIME;
! 		smb_UnixTimeFromDosUTime(&attr.clientModTime, dosTime);
! 	}
! 	if ((newScp->unixModeBits & 0222) && (attribute & 1) != 0) {
! 		/* we're told to make a writable file read-only */
! 		attr.unixModeBits = newScp->unixModeBits & ~0222;
! 		attr.mask |= CM_ATTRMASK_UNIXMODEBITS;
! 	}
! 	else if ((newScp->unixModeBits & 0222) == 0 && (attribute & 1) == 0) {
! 		/* we're told to make a read-only file writable */
! 		attr.unixModeBits = newScp->unixModeBits | 0222;
! 		attr.mask |= CM_ATTRMASK_UNIXMODEBITS;
! 	}
! 	lock_ReleaseMutex(&newScp->mx);
! 
! 	/* now call setattr */
! 	if (attr.mask)
! 		code = cm_SetAttr(newScp, &attr, userp, &req);
! 	else
! 		code = 0;
!         
! 	cm_ReleaseSCache(newScp);
! 	cm_ReleaseUser(userp);
  
! 	return code;
! }
  
! long smb_ReceiveCoreGetFileAttributes(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
! {
! 	char *pathp;
! 	long code = 0;
! 	cm_scache_t *rootScp;
! 	cm_scache_t *newScp, *dscp;
! 	time_t dosTime;
! 	int attrs;
! 	cm_user_t *userp;
! 	int caseFold;
! 	char *tidPathp;
! 	cm_space_t *spacep;
! 	char *lastComp;
! 	cm_req_t req;
! 
! 	cm_InitReq(&req);
! 
! 	pathp = smb_GetSMBData(inp, NULL);
! 	pathp = smb_ParseASCIIBlock(pathp, NULL);
!         
! 	if (!pathp) {
! 		return CM_ERROR_BADSMB;
! 	}
!         
! 	if (*pathp == 0)		/* null path */
! 		pathp = "\\";
! 
! 	osi_Log1(smb_logp, "SMB receive getfile attributes path %s",
! 			 osi_LogSaveString(smb_logp, pathp));
  
! 	rootScp = cm_rootSCachep;
!         
! 	userp = smb_GetUser(vcp, inp);
  
! 	/* we shouldn't need this for V3 requests, but we seem to */
! 	caseFold = CM_FLAG_CASEFOLD;
  
! 	code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!     if(code) {
          cm_ReleaseUser(userp);
!         return CM_ERROR_NOSUCHFILE;
      }
  
! 	/*
! 	 * XXX Strange hack XXX
! 	 *
! 	 * As of Patch 5 (16 July 97), we are having the following problem:
! 	 * In NT Explorer 4.0, whenever we click on a directory, AFS gets
! 	 * requests to look up "desktop.ini" in all the subdirectories.
! 	 * This can cause zillions of timeouts looking up non-existent cells
! 	 * and volumes, especially in the top-level directory.
! 	 *
! 	 * We have not found any way to avoid this or work around it except
! 	 * to explicitly ignore the requests for mount points that haven't
! 	 * yet been evaluated and for directories that haven't yet been
! 	 * fetched.
! 	 *
! 	 * We should modify this hack to provide a fake desktop.ini file
! 	 * http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/programmersguide/shell_basics/shell_basics_extending/custom.asp
! 	 */
! 	spacep = inp->spacep;
! 	smb_StripLastComponent(spacep->data, &lastComp, pathp);
! 	if (lastComp && stricmp(lastComp, "\\desktop.ini") == 0) {
! 		code = cm_NameI(rootScp, spacep->data,
! 						caseFold | CM_FLAG_DIRSEARCH | CM_FLAG_FOLLOW,
! 						userp, tidPathp, &req, &dscp);
! 		if (code == 0) {
! 			if (dscp->fileType == CM_SCACHETYPE_MOUNTPOINT
! 			    && !dscp->mountRootFidp)
! 				code = CM_ERROR_NOSUCHFILE;
! 			else if (dscp->fileType == CM_SCACHETYPE_DIRECTORY) {
! 				cm_buf_t *bp = buf_Find(dscp, &hzero);
! 				if (bp)
! 					buf_Release(bp);
! 				else
! 					code = CM_ERROR_NOSUCHFILE;
! 			}
! 			cm_ReleaseSCache(dscp);
! 			if (code) {
! 				cm_ReleaseUser(userp);
! 				return code;
! 			}
! 		}
! 	}
! 
! 	code = cm_NameI(rootScp, pathp, caseFold | CM_FLAG_FOLLOW, userp,
! 					tidPathp, &req, &newScp);
! 
! 	if (code) {
! 		cm_ReleaseUser(userp);
! 		return code;
! 	}
!         
! 	/* now lock the vnode with a callback; returns with newScp locked */
! 	lock_ObtainMutex(&newScp->mx);
! 	code = cm_SyncOp(newScp, NULL, userp, &req, 0,
! 					 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
! 	if (code) {
! 		lock_ReleaseMutex(&newScp->mx);
! 		cm_ReleaseSCache(newScp);
! 		cm_ReleaseUser(userp);
! 		return code;
! 	}
! 
! #ifdef undef
!     /* use smb_Attributes instead.   Also the fact that a file is 
! 	 * in a readonly volume doesn't mean it shojuld be marked as RO 
! 	 */
! 	if (newScp->fileType == CM_SCACHETYPE_DIRECTORY
! 		|| newScp->fileType == CM_SCACHETYPE_MOUNTPOINT)
! 		attrs = SMB_ATTR_DIRECTORY;
! 	else
! 		attrs = 0;
! 	if ((newScp->unixModeBits & 0222) == 0 || (newScp->flags & CM_SCACHEFLAG_RO))
! 		attrs |= SMB_ATTR_READONLY;	/* turn on read-only flag */
! #else
!     attrs = smb_Attributes(newScp);
! #endif
  
! 	smb_SetSMBParm(outp, 0, attrs);
          
! 	smb_DosUTimeFromUnixTime(&dosTime, newScp->clientModTime);
! 	smb_SetSMBParm(outp, 1, dosTime & 0xffff);
! 	smb_SetSMBParm(outp, 2, (dosTime>>16) & 0xffff);
! 	smb_SetSMBParm(outp, 3, newScp->length.LowPart & 0xffff);
! 	smb_SetSMBParm(outp, 4, (newScp->length.LowPart >> 16) & 0xffff);
! 	smb_SetSMBParm(outp, 5, 0);
! 	smb_SetSMBParm(outp, 6, 0);
! 	smb_SetSMBParm(outp, 7, 0);
! 	smb_SetSMBParm(outp, 8, 0);
! 	smb_SetSMBParm(outp, 9, 0);
! 	smb_SetSMBDataLength(outp, 0);
! 	lock_ReleaseMutex(&newScp->mx);
  
! 	cm_ReleaseSCache(newScp);
! 	cm_ReleaseUser(userp);
          
! 	return 0;
  }	
  
  long smb_ReceiveCoreTreeDisconnect(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
! 	smb_tid_t *tidp;
          
! 	osi_Log0(smb_logp, "SMB receive tree disconnect");
  
! 	/* find the tree and free it */
! 	tidp = smb_FindTID(vcp, ((smb_t *)inp)->tid, 0);
! 	if (tidp) {
! 		lock_ObtainMutex(&tidp->mx);
! 		tidp->flags |= SMB_TIDFLAG_DELETE;
! 		lock_ReleaseMutex(&tidp->mx);
! 		smb_ReleaseTID(tidp);
! 	}
  
! 	return 0;
  }
  
  long smb_ReceiveCoreOpen(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
! 	smb_fid_t *fidp;
      char *pathp;
! 	char *lastNamep;
      int share;
      int attribute;
! 	long code = 0;
      cm_user_t *userp;
      cm_scache_t *scp;
      time_t dosTime;
      int caseFold;
! 	cm_space_t *spacep;
! 	char *tidPathp;
! 	cm_req_t req;
  
! 	cm_InitReq(&req);
  
      pathp = smb_GetSMBData(inp, NULL);
      pathp = smb_ParseASCIIBlock(pathp, NULL);
--- 3217,4104 ----
                  *dptr++ = SMB_ATTR_HIDDEN;
              continue;
          }
!         lock_ObtainMutex(&scp->mx);
!         code = cm_SyncOp(scp, NULL, userp, reqp, 0,
!                           CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
!         if (code) {	
!             lock_ReleaseMutex(&scp->mx);
!             cm_ReleaseSCache(scp);
!             if (patchp->flags & SMB_DIRLISTPATCH_DOTFILE)
                  *dptr++ = SMB_ATTR_HIDDEN;
!             continue;
!         }
  
!         attr = smb_Attributes(scp);
          /* check hidden attribute (the flag is only ON when dot file hiding is on ) */
!         if (patchp->flags & SMB_DIRLISTPATCH_DOTFILE)
              attr |= SMB_ATTR_HIDDEN;
          *dptr++ = attr;
  
          /* get dos time */
!         smb_SearchTimeFromUnixTime(&dosTime, scp->clientModTime);
                  
!         /* copy out time */
!         shortTemp = dosTime & 0xffff;
!         *((u_short *)dptr) = shortTemp;
!         dptr += 2;
! 
!         /* and copy out date */
!         shortTemp = (dosTime>>16) & 0xffff;
!         *((u_short *)dptr) = shortTemp;
!         dptr += 2;
                  
!         /* copy out file length */
!         *((u_long *)dptr) = scp->length.LowPart;
!         dptr += 4;
!         lock_ReleaseMutex(&scp->mx);
!         cm_ReleaseSCache(scp);
!     }
!         
!     /* now free the patches */
!     for (patchp = *dirPatchespp; patchp; patchp = npatchp) {
!         npatchp = (smb_dirListPatch_t *) osi_QNext(&patchp->q);
!         free(patchp);
!     }	
          
!     /* and mark the list as empty */
!     *dirPatchespp = NULL;
  
!     return code;
  }
  
  long smb_ReceiveCoreSearchDir(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
!     int attribute;
!     long nextCookie;
!     char *tp;
!     long code = 0;
!     char *pathp;
!     cm_dirEntry_t *dep;
!     int maxCount;
!     smb_dirListPatch_t *dirListPatchesp;
!     smb_dirListPatch_t *curPatchp;
!     int dataLength;
!     cm_buf_t *bufferp;
!     long temp;
!     osi_hyper_t dirLength;
!     osi_hyper_t bufferOffset;
!     osi_hyper_t curOffset;
!     osi_hyper_t thyper;
!     unsigned char *inCookiep;
!     smb_dirSearch_t *dsp;
!     cm_scache_t *scp;
!     long entryInDir;
!     long entryInBuffer;
!     unsigned long clientCookie;
!     cm_pageHeader_t *pageHeaderp;
!     cm_user_t *userp = NULL;
!     int slotInPage;
!     char shortName[13];
!     char *actualName;
!     char *shortNameEnd;
!     char mask[11];
!     int returnedNames;
!     long nextEntryCookie;
!     int numDirChunks;		/* # of 32 byte dir chunks in this entry */
!     char resByte;			/* reserved byte from the cookie */
!     char *op;			/* output data ptr */
!     char *origOp;			/* original value of op */
!     cm_space_t *spacep;		/* for pathname buffer */
!     int starPattern;
!     int rootPath = 0;
!     int caseFold;
!     char *tidPathp;
!     cm_req_t req;
!     cm_fid_t fid;
!     int fileType;
  
!     cm_InitReq(&req);
  
!     maxCount = smb_GetSMBParm(inp, 0);
  
!     dirListPatchesp = NULL;
          
!     caseFold = CM_FLAG_CASEFOLD;
  
!     tp = smb_GetSMBData(inp, NULL);
!     pathp = smb_ParseASCIIBlock(tp, &tp);
!     inCookiep = smb_ParseVblBlock(tp, &tp, &dataLength);
  
!     /* bail out if request looks bad */
!     if (!tp || !pathp) {
!         return CM_ERROR_BADSMB;
      }
  
!     /* We can handle long names */
!     if (vcp->flags & SMB_VCFLAG_USENT)
!         ((smb_t *)outp)->flg2 |= 0x40;	/* IS_LONG_NAME */
  
!     /* make sure we got a whole search status */
!     if (dataLength < 21) {
!         nextCookie = 0;		/* start at the beginning of the dir */
!         resByte = 0;
!         clientCookie = 0;
!         attribute = smb_GetSMBParm(inp, 1);
  
!         /* handle volume info in another function */
!         if (attribute & 0x8)
!             return smb_ReceiveCoreSearchVolume(vcp, inp, outp);
  
!         osi_Log2(smb_logp, "SMB receive search dir count %d [%s]",
!                   maxCount, osi_LogSaveString(smb_logp, pathp));
  
!         if (*pathp == 0) {	/* null pathp, treat as root dir */
!             if (!(attribute & SMB_ATTR_DIRECTORY))	/* exclude dirs */
!                 return CM_ERROR_NOFILES;
!             rootPath = 1;
!         }
  
!         dsp = smb_NewDirSearch(0);
!         dsp->attribute = attribute;
!         smb_Get8Dot3MaskFromPath(mask, pathp);
!         memcpy(dsp->mask, mask, 11);
  
!         /* track if this is likely to match a lot of entries */
!         if (smb_IsStarMask(mask)) starPattern = 1;
!         else starPattern = 0;
!     }	
!     else {
!         /* pull the next cookie value out of the search status block */
!         nextCookie = inCookiep[13] + (inCookiep[14]<<8) + (inCookiep[15]<<16)
!             + (inCookiep[16]<<24);
!         dsp = smb_FindDirSearch(inCookiep[12]);
!         if (!dsp) {
!             /* can't find dir search status; fatal error */
!             return CM_ERROR_BADFD;
!         }
!         attribute = dsp->attribute;
!         resByte = inCookiep[0];
  
!         /* copy out client cookie, in host byte order.  Don't bother
!          * interpreting it, since we're just passing it through, anyway.
!          */
!         memcpy(&clientCookie, &inCookiep[17], 4);
  
!         memcpy(mask, dsp->mask, 11);
  
!         /* assume we're doing a star match if it has continued for more
!          * than one call.
!          */
!         starPattern = 1;
!     }
  
!     osi_Log3(smb_logp, "SMB dir search cookie 0x%x, connection %d, attr 0x%x",
!              nextCookie, dsp->cookie, attribute);
  
!     userp = smb_GetUser(vcp, inp);
  
!     /* try to get the vnode for the path name next */
!     lock_ObtainMutex(&dsp->mx);
!     if (dsp->scp) {
!         scp = dsp->scp;
!         cm_HoldSCache(scp);
!         code = 0;
!     }
!     else {
!         spacep = inp->spacep;
!         smb_StripLastComponent(spacep->data, NULL, pathp);
!         lock_ReleaseMutex(&dsp->mx);
!         code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!         if (code) {
!             lock_ReleaseMutex(&dsp->mx);
!             cm_ReleaseUser(userp);
!             smb_DeleteDirSearch(dsp);
!             smb_ReleaseDirSearch(dsp);
!             return CM_ERROR_NOFILES;
!         }
!         code = cm_NameI(cm_rootSCachep, spacep->data,
!                         caseFold | CM_FLAG_FOLLOW, userp, tidPathp, &req, &scp);
!         lock_ObtainMutex(&dsp->mx);
!         if (code == 0) {
!             if (dsp->scp != 0) cm_ReleaseSCache(dsp->scp);
!             dsp->scp = scp;
!             /* we need one hold for the entry we just stored into,
!              * and one for our own processing.  When we're done with this
!              * function, we'll drop the one for our own processing.
!              * We held it once from the namei call, and so we do another hold
!              * now.
!              */
!             cm_HoldSCache(scp);
!             lock_ObtainMutex(&scp->mx);
!             if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
!                  && LargeIntegerGreaterOrEqualToZero(scp->bulkStatProgress)) {
!                 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
!                 dsp->flags |= SMB_DIRSEARCH_BULKST;
!             }
!             lock_ReleaseMutex(&scp->mx);
!         }
!     }
!     lock_ReleaseMutex(&dsp->mx);
!     if (code) {
          cm_ReleaseUser(userp);
!         smb_DeleteDirSearch(dsp);
!         smb_ReleaseDirSearch(dsp);
!         return code;
      }
  
!     /* reserves space for parameter; we'll adjust it again later to the
!      * real count of the # of entries we returned once we've actually
!      * assembled the directory listing.
!      */
!     smb_SetSMBParm(outp, 0, 0);
  
!     /* get the directory size */
!     lock_ObtainMutex(&scp->mx);
!     code = cm_SyncOp(scp, NULL, userp, &req, 0,
!                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
!     if (code) {
!         lock_ReleaseMutex(&scp->mx);
!         cm_ReleaseSCache(scp);
!         cm_ReleaseUser(userp);
!         smb_DeleteDirSearch(dsp);
!         smb_ReleaseDirSearch(dsp);
!         return code;
!     }
          
!     dirLength = scp->length;
!     bufferp = NULL;
!     bufferOffset.LowPart = bufferOffset.HighPart = 0;
!     curOffset.HighPart = 0;
!     curOffset.LowPart = nextCookie;
!     origOp = op = smb_GetSMBData(outp, NULL);
!     /* and write out the basic header */
!     *op++ = 5;		/* variable block */
!     op += 2;		/* skip vbl block length; we'll fill it in later */
!     code = 0;
!     returnedNames = 0;
!     while (1) {
!         /* make sure that curOffset.LowPart doesn't point to the first
!          * 32 bytes in the 2nd through last dir page, and that it doesn't
!          * point at the first 13 32-byte chunks in the first dir page,
!          * since those are dir and page headers, and don't contain useful
!          * information.
!          */
!         temp = curOffset.LowPart & (2048-1);
!         if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
!             /* we're in the first page */
!             if (temp < 13*32) temp = 13*32;
!         }
!         else {
!             /* we're in a later dir page */
!             if (temp < 32) temp = 32;
!         }
  
!         /* make sure the low order 5 bits are zero */
!         temp &= ~(32-1);
! 
!         /* now put temp bits back ito curOffset.LowPart */
!         curOffset.LowPart &= ~(2048-1);
!         curOffset.LowPart |= temp;
! 
!         /* check if we've returned all the names that will fit in the
!          * response packet.
!          */
!         if (returnedNames >= maxCount) 
!             break;
!                 
!         /* check if we've passed the dir's EOF */
!         if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength)) break;
! 
!         /* see if we can use the bufferp we have now; compute in which page
!          * the current offset would be, and check whether that's the offset
!          * of the buffer we have.  If not, get the buffer.
!          */
!         thyper.HighPart = curOffset.HighPart;
!         thyper.LowPart = curOffset.LowPart & ~(buf_bufferSize-1);
!         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
!             /* wrong buffer */
!             if (bufferp) {
!                 buf_Release(bufferp);
!                 bufferp = NULL;
!             }	
!             lock_ReleaseMutex(&scp->mx);
!             lock_ObtainRead(&scp->bufCreateLock);
!             code = buf_Get(scp, &thyper, &bufferp);
!             lock_ReleaseRead(&scp->bufCreateLock);
! 
!             /* now, if we're doing a star match, do bulk fetching of all of 
!              * the status info for files in the dir.
!              */
!             if (starPattern) {
!                 smb_ApplyDirListPatches(&dirListPatchesp, userp,
!                                          &req);
!                 if ((dsp->flags & SMB_DIRSEARCH_BULKST) &&
!                      LargeIntegerGreaterThanOrEqualTo(thyper, 
!                                                       scp->bulkStatProgress)) {
!                     /* Don't bulk stat if risking timeout */
!                     int now = GetCurrentTime();
!                     if (now - req.startTime > 5000) {
!                         scp->bulkStatProgress = thyper;
!                         scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
!                         dsp->flags &= ~SMB_DIRSEARCH_BULKST;
!                     } else
!                         cm_TryBulkStat(scp, &thyper, userp, &req);
!                 }
!             }
! 
!             lock_ObtainMutex(&scp->mx);
!             if (code) 
!                 break;
!             bufferOffset = thyper;
! 
!             /* now get the data in the cache */
!             while (1) {
!                 code = cm_SyncOp(scp, bufferp, userp, &req,
!                                  PRSFS_LOOKUP,
!                                  CM_SCACHESYNC_NEEDCALLBACK |
!                                  CM_SCACHESYNC_READ);
!                 if (code) break;
!                                 
!                 if (cm_HaveBuffer(scp, bufferp, 0)) break;
! 
!                 /* otherwise, load the buffer and try again */
!                 code = cm_GetBuffer(scp, bufferp, NULL, userp, &req);
!                 if (code) break;
!             }
!             if (code) {
!                 buf_Release(bufferp);
!                 bufferp = NULL;
!                 break;
!             }
!         }	/* if (wrong buffer) ... */
! 
!         /* now we have the buffer containing the entry we're interested in; copy
!          * it out if it represents a non-deleted entry.
!          */
!         entryInDir = curOffset.LowPart & (2048-1);
!         entryInBuffer = curOffset.LowPart & (buf_bufferSize - 1);
! 
!         /* page header will help tell us which entries are free.  Page header
!          * can change more often than once per buffer, since AFS 3 dir page size
!          * may be less than (but not more than a buffer package buffer.
!          */
!         temp = curOffset.LowPart & (buf_bufferSize - 1);  /* only look intra-buffer */
!         temp &= ~(2048 - 1);	/* turn off intra-page bits */
!         pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
! 
!         /* now determine which entry we're looking at in the page.  If it is
!          * free (there's a free bitmap at the start of the dir), we should
!          * skip these 32 bytes.
!          */
!         slotInPage = (entryInDir & 0x7e0) >> 5;
!         if (!(pageHeaderp->freeBitmap[slotInPage>>3] & (1 << (slotInPage & 0x7)))) {
!             /* this entry is free */
!             numDirChunks = 1;		/* only skip this guy */
!             goto nextEntry;
!         }
! 
!         tp = bufferp->datap + entryInBuffer;
!         dep = (cm_dirEntry_t *) tp;		/* now points to AFS3 dir entry */
! 
!         /* while we're here, compute the next entry's location, too,
!          * since we'll need it when writing out the cookie into the dir
!          * listing stream.
!          *
!          * XXXX Probably should do more sanity checking.
!          */
!         numDirChunks = cm_NameEntries(dep->name, NULL);
! 
!         /* compute the offset of the cookie representing the next entry */
!         nextEntryCookie = curOffset.LowPart + (CM_DIR_CHUNKSIZE * numDirChunks);
! 
!         /* Compute 8.3 name if necessary */
!         actualName = dep->name;
!         if (dep->fid.vnode != 0 && !cm_Is8Dot3(actualName)) {
!             cm_Gen8Dot3Name(dep, shortName, &shortNameEnd);
!             actualName = shortName;
!         }
! 
!         if (dep->fid.vnode != 0 && smb_Match8Dot3Mask(actualName, mask)) {
!             /* this is one of the entries to use: it is not deleted
!              * and it matches the star pattern we're looking for.
!              */
! 
!             /* Eliminate entries that don't match requested
!              * attributes */
! 
!             /* no hidden files */
!             if(smb_hideDotFiles && !(dsp->attribute & SMB_ATTR_HIDDEN) && smb_IsDotFile(actualName))
!                 goto nextEntry;
! 
!             if (!(dsp->attribute & SMB_ATTR_DIRECTORY))  /* no directories */
!             {
!                 /* We have already done the cm_TryBulkStat above */
!                 fid.cell = scp->fid.cell;
!                 fid.volume = scp->fid.volume;
!                 fid.vnode = ntohl(dep->fid.vnode);
!                 fid.unique = ntohl(dep->fid.unique);
!                 fileType = cm_FindFileType(&fid);
!                 osi_Log2(smb_logp, "smb_ReceiveCoreSearchDir: file %s "
!                          "has filetype %d", osi_LogSaveString(smb_logp, dep->name),
!                           fileType);
!                 if (fileType == CM_SCACHETYPE_DIRECTORY)
!                     goto nextEntry;
!             }
! 
!             *op++ = resByte;
!             memcpy(op, mask, 11); op += 11;
!             *op++ = (char) dsp->cookie;	/* they say it must be non-zero */
!             *op++ = nextEntryCookie & 0xff;
!             *op++ = (nextEntryCookie>>8) & 0xff;
!             *op++ = (nextEntryCookie>>16) & 0xff;
!             *op++ = (nextEntryCookie>>24) & 0xff;
!             memcpy(op, &clientCookie, 4); op += 4;
! 
!             /* now we emit the attribute.  This is sort of tricky,
!              * since we need to really stat the file to find out
!              * what type of entry we've got.  Right now, we're
!              * copying out data from a buffer, while holding the
!              * scp locked, so it isn't really convenient to stat
!              * something now.  We'll put in a place holder now,
!              * and make a second pass before returning this to get
!              * the real attributes.  So, we just skip the data for
!              * now, and adjust it later.  We allocate a patch
!              * record to make it easy to find this point later.
!              * The replay will happen at a time when it is safe to
!              * unlock the directory.
!              */
!             curPatchp = malloc(sizeof(*curPatchp));
!             osi_QAdd((osi_queue_t **) &dirListPatchesp, &curPatchp->q);
!             curPatchp->dptr = op;
!             curPatchp->fid.cell = scp->fid.cell;
!             curPatchp->fid.volume = scp->fid.volume;
!             curPatchp->fid.vnode = ntohl(dep->fid.vnode);
!             curPatchp->fid.unique = ntohl(dep->fid.unique);
! 
!             /* do hidden attribute here since name won't be around when applying
!              * dir list patches
!              */
! 
!             if ( smb_hideDotFiles && smb_IsDotFile(actualName) )
!                 curPatchp->flags = SMB_DIRLISTPATCH_DOTFILE;
!             else
!                 curPatchp->flags = 0;
! 
!             op += 9;	/* skip attr, time, date and size */
! 
!             /* zero out name area.  The spec says to pad with
!              * spaces, but Samba doesn't, and neither do we.
!              */
!             memset(op, 0, 13);
! 
!             /* finally, we get to copy out the name; we know that
!              * it fits in 8.3 or the pattern wouldn't match, but it
!              * never hurts to be sure.
!              */
!             strncpy(op, actualName, 13);
! 
!             /* Uppercase if requested by client */
!             if ((((smb_t *)inp)->flg2 & 1) == 0)
!                 _strupr(op);
! 
!             op += 13;
! 
!             /* now, adjust the # of entries copied */
!             returnedNames++;
!         }	/* if we're including this name */
! 
!       nextEntry:
!         /* and adjust curOffset to be where the new cookie is */
!         thyper.HighPart = 0;
!         thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
!         curOffset = LargeIntegerAdd(thyper, curOffset);
!     }		/* while copying data for dir listing */
! 
!     /* release the mutex */
!     lock_ReleaseMutex(&scp->mx);
!     if (bufferp) buf_Release(bufferp);
! 
!     /* apply and free last set of patches; if not doing a star match, this
!      * will be empty, but better safe (and freeing everything) than sorry.
!      */
!     smb_ApplyDirListPatches(&dirListPatchesp, userp, &req);
! 
!     /* special return code for unsuccessful search */
!     if (code == 0 && dataLength < 21 && returnedNames == 0)
!         code = CM_ERROR_NOFILES;
! 
!     osi_Log2(smb_logp, "SMB search dir done, %d names, code %d",
!              returnedNames, code);
! 
!     if (code != 0) {
!         smb_DeleteDirSearch(dsp);
!         smb_ReleaseDirSearch(dsp);
!         cm_ReleaseSCache(scp);
!         cm_ReleaseUser(userp);
!         return code;
!     }
! 
!     /* finalize the output buffer */
!     smb_SetSMBParm(outp, 0, returnedNames);
!     temp = (long) (op - origOp);
!     smb_SetSMBDataLength(outp, temp);
! 
!     /* the data area is a variable block, which has a 5 (already there)
!      * followed by the length of the # of data bytes.  We now know this to
!      * be "temp," although that includes the 3 bytes of vbl block header.
!      * Deduct for them and fill in the length field.
!      */
!     temp -= 3;		/* deduct vbl block info */
!     osi_assert(temp == (43 * returnedNames));
!     origOp[1] = temp & 0xff;
!     origOp[2] = (temp>>8) & 0xff;
!     if (returnedNames == 0) 
!         smb_DeleteDirSearch(dsp);
!     smb_ReleaseDirSearch(dsp);
!     cm_ReleaseSCache(scp);
!     cm_ReleaseUser(userp);
!     return code;
! }	
! 
! /* verify that this is a valid path to a directory.  I don't know why they
!  * don't use the get file attributes call.
!  */
! long smb_ReceiveCoreCheckPath(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
! {
!     char *pathp;
!     long code = 0;
!     cm_scache_t *rootScp;
!     cm_scache_t *newScp;
!     cm_user_t *userp;
!     unsigned int attrs;
!     int caseFold;
!     char *tidPathp;
!     cm_req_t req;
! 
!     cm_InitReq(&req);
! 
!     pathp = smb_GetSMBData(inp, NULL);
!     pathp = smb_ParseASCIIBlock(pathp, NULL);
!     osi_Log1(smb_logp, "SMB receive check path %s",
!              osi_LogSaveString(smb_logp, pathp));
! 
!     if (!pathp) {
!         return CM_ERROR_BADFD;
!     }
!         
!     rootScp = cm_rootSCachep;
!         
!     userp = smb_GetUser(vcp, inp);
! 
!     caseFold = CM_FLAG_CASEFOLD;
! 
!     code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!     if(code) {
!         cm_ReleaseUser(userp);
!         return CM_ERROR_NOSUCHPATH;
!     }
!     code = cm_NameI(rootScp, pathp,
!                     caseFold | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH,
!                     userp, tidPathp, &req, &newScp);
! 
!     if (code) {
!         cm_ReleaseUser(userp);
!         return code;
!     }
!         
!     /* now lock the vnode with a callback; returns with newScp locked */
!     lock_ObtainMutex(&newScp->mx);
!     code = cm_SyncOp(newScp, NULL, userp, &req, PRSFS_LOOKUP,
!                      CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
!     if (code && code != CM_ERROR_NOACCESS) {
!         lock_ReleaseMutex(&newScp->mx);
!         cm_ReleaseSCache(newScp);
!         cm_ReleaseUser(userp);
!         return code;
!     }
! 
!     attrs = smb_Attributes(newScp);
! 
!     if (!(attrs & 0x10))
!         code = CM_ERROR_NOTDIR;
! 
!     lock_ReleaseMutex(&newScp->mx);
! 
!     cm_ReleaseSCache(newScp);
!     cm_ReleaseUser(userp);
!     return code;
! }	
! 
! long smb_ReceiveCoreSetFileAttributes(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
! {
!     char *pathp;
!     long code = 0;
!     cm_scache_t *rootScp;
!     unsigned short attribute;
!     cm_attr_t attr;
!     cm_scache_t *newScp;
!     time_t dosTime;
!     cm_user_t *userp;
!     int caseFold;
!     char *tidPathp;
!     cm_req_t req;
! 
!     cm_InitReq(&req);
! 
!     /* decode basic attributes we're passed */
!     attribute = smb_GetSMBParm(inp, 0);
!     dosTime = smb_GetSMBParm(inp, 1) | (smb_GetSMBParm(inp, 2) << 16);
! 
!     pathp = smb_GetSMBData(inp, NULL);
!     pathp = smb_ParseASCIIBlock(pathp, NULL);
! 
!     if (!pathp) {
!         return CM_ERROR_BADSMB;
!     }
!         
!     osi_Log2(smb_logp, "SMB receive setfile attributes time %d, attr 0x%x",
!              dosTime, attribute);
! 
!     rootScp = cm_rootSCachep;
!         
!     userp = smb_GetUser(vcp, inp);
! 
!     caseFold = CM_FLAG_CASEFOLD;
! 
!     code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!     if (code) {
!         cm_ReleaseUser(userp);
!         return CM_ERROR_NOSUCHFILE;
!     }
!     code = cm_NameI(rootScp, pathp, caseFold | CM_FLAG_FOLLOW, userp,
!                     tidPathp, &req, &newScp);
! 
!     if (code) {
!         cm_ReleaseUser(userp);
!         return code;
!     }
! 	
!     /* now lock the vnode with a callback; returns with newScp locked; we
!      * need the current status to determine what the new status is, in some
!      * cases.
!      */
!     lock_ObtainMutex(&newScp->mx);
!     code = cm_SyncOp(newScp, NULL, userp, &req, 0,
!                      CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
!     if (code) {
!         lock_ReleaseMutex(&newScp->mx);
!         cm_ReleaseSCache(newScp);
!         cm_ReleaseUser(userp);
!         return code;
!     }
! 
!     /* Check for RO volume */
!     if (newScp->flags & CM_SCACHEFLAG_RO) {
!         lock_ReleaseMutex(&newScp->mx);
!         cm_ReleaseSCache(newScp);
!         cm_ReleaseUser(userp);
!         return CM_ERROR_READONLY;
!     }
! 
!     /* prepare for setattr call */
!     attr.mask = 0;
!     if (dosTime != 0) {
!         attr.mask |= CM_ATTRMASK_CLIENTMODTIME;
!         smb_UnixTimeFromDosUTime(&attr.clientModTime, dosTime);
!     }
!     if ((newScp->unixModeBits & 0222) && (attribute & 1) != 0) {
!         /* we're told to make a writable file read-only */
!         attr.unixModeBits = newScp->unixModeBits & ~0222;
!         attr.mask |= CM_ATTRMASK_UNIXMODEBITS;
!     }
!     else if ((newScp->unixModeBits & 0222) == 0 && (attribute & 1) == 0) {
!         /* we're told to make a read-only file writable */
!         attr.unixModeBits = newScp->unixModeBits | 0222;
!         attr.mask |= CM_ATTRMASK_UNIXMODEBITS;
!     }
!     lock_ReleaseMutex(&newScp->mx);
! 
!     /* now call setattr */
!     if (attr.mask)
!         code = cm_SetAttr(newScp, &attr, userp, &req);
!     else
!         code = 0;
!         
!     cm_ReleaseSCache(newScp);
!     cm_ReleaseUser(userp);
! 
!     return code;
! }
! 
! long smb_ReceiveCoreGetFileAttributes(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
! {
!     char *pathp;
!     long code = 0;
!     cm_scache_t *rootScp;
!     cm_scache_t *newScp, *dscp;
!     time_t dosTime;
!     int attrs;
!     cm_user_t *userp;
!     int caseFold;
!     char *tidPathp;
!     cm_space_t *spacep;
!     char *lastComp;
!     cm_req_t req;
! 
!     cm_InitReq(&req);
! 
!     pathp = smb_GetSMBData(inp, NULL);
!     pathp = smb_ParseASCIIBlock(pathp, NULL);
! 
!     if (!pathp) {
!         return CM_ERROR_BADSMB;
!     }
!         
!     if (*pathp == 0)		/* null path */
!         pathp = "\\";
! 
!     osi_Log1(smb_logp, "SMB receive getfile attributes path %s",
!              osi_LogSaveString(smb_logp, pathp));
! 
!     rootScp = cm_rootSCachep;
!         
!     userp = smb_GetUser(vcp, inp);
! 
!     /* we shouldn't need this for V3 requests, but we seem to */
!     caseFold = CM_FLAG_CASEFOLD;
! 
!     code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!     if (code) {
!         cm_ReleaseUser(userp);
!         return CM_ERROR_NOSUCHFILE;
!     }
! 
!     /*
!      * XXX Strange hack XXX
!      *
!      * As of Patch 5 (16 July 97), we are having the following problem:
!      * In NT Explorer 4.0, whenever we click on a directory, AFS gets
!      * requests to look up "desktop.ini" in all the subdirectories.
!      * This can cause zillions of timeouts looking up non-existent cells
!      * and volumes, especially in the top-level directory.
!      *
!      * We have not found any way to avoid this or work around it except
!      * to explicitly ignore the requests for mount points that haven't
!      * yet been evaluated and for directories that haven't yet been
!      * fetched.
!      *
!      * We should modify this hack to provide a fake desktop.ini file
!      * http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/programmersguide/shell_basics/shell_basics_extending/custom.asp
!      */
!     spacep = inp->spacep;
!     smb_StripLastComponent(spacep->data, &lastComp, pathp);
!     if (lastComp && stricmp(lastComp, "\\desktop.ini") == 0) {
!         code = cm_NameI(rootScp, spacep->data,
!                         caseFold | CM_FLAG_DIRSEARCH | CM_FLAG_FOLLOW,
!                         userp, tidPathp, &req, &dscp);
!         if (code == 0) {
!             if (dscp->fileType == CM_SCACHETYPE_MOUNTPOINT &&
!                 !dscp->mountRootFidp)
!                 code = CM_ERROR_NOSUCHFILE;
!             else if (dscp->fileType == CM_SCACHETYPE_DIRECTORY) {
!                 cm_buf_t *bp = buf_Find(dscp, &hzero);
!                 if (bp)
!                     buf_Release(bp);
!                 else
!                     code = CM_ERROR_NOSUCHFILE;
!             }
!             cm_ReleaseSCache(dscp);
!             if (code) {
!                 cm_ReleaseUser(userp);
!                 return code;
!             }
!         }
!     }
! 
!     code = cm_NameI(rootScp, pathp, caseFold | CM_FLAG_FOLLOW, userp,
!                     tidPathp, &req, &newScp);
!     if (code) {
!         cm_ReleaseUser(userp);
!         return code;
!     }
!         
!     /* now lock the vnode with a callback; returns with newScp locked */
!     lock_ObtainMutex(&newScp->mx);
!     code = cm_SyncOp(newScp, NULL, userp, &req, 0,
!                      CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
!     if (code) {
!         lock_ReleaseMutex(&newScp->mx);
!         cm_ReleaseSCache(newScp);
!         cm_ReleaseUser(userp);
!         return code;
!     }
! 
! #ifdef undef
!     /* use smb_Attributes instead.   Also the fact that a file is 
!      * in a readonly volume doesn't mean it shojuld be marked as RO 
!      */
!     if (newScp->fileType == CM_SCACHETYPE_DIRECTORY ||
!         newScp->fileType == CM_SCACHETYPE_MOUNTPOINT)
!         attrs = SMB_ATTR_DIRECTORY;
!     else
!         attrs = 0;
!     if ((newScp->unixModeBits & 0222) == 0 || (newScp->flags & CM_SCACHEFLAG_RO))
!         attrs |= SMB_ATTR_READONLY;	/* turn on read-only flag */
! #else
!     attrs = smb_Attributes(newScp);
! #endif
! 
!     smb_SetSMBParm(outp, 0, attrs);
          
!     smb_DosUTimeFromUnixTime(&dosTime, newScp->clientModTime);
!     smb_SetSMBParm(outp, 1, dosTime & 0xffff);
!     smb_SetSMBParm(outp, 2, (dosTime>>16) & 0xffff);
!     smb_SetSMBParm(outp, 3, newScp->length.LowPart & 0xffff);
!     smb_SetSMBParm(outp, 4, (newScp->length.LowPart >> 16) & 0xffff);
!     smb_SetSMBParm(outp, 5, 0);
!     smb_SetSMBParm(outp, 6, 0);
!     smb_SetSMBParm(outp, 7, 0);
!     smb_SetSMBParm(outp, 8, 0);
!     smb_SetSMBParm(outp, 9, 0);
!     smb_SetSMBDataLength(outp, 0);
!     lock_ReleaseMutex(&newScp->mx);
! 
!     cm_ReleaseSCache(newScp);
!     cm_ReleaseUser(userp);
! 
!     return 0;
  }	
  
  long smb_ReceiveCoreTreeDisconnect(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
!     smb_tid_t *tidp;
          
!     osi_Log0(smb_logp, "SMB receive tree disconnect");
  
!     /* find the tree and free it */
!     tidp = smb_FindTID(vcp, ((smb_t *)inp)->tid, 0);
!     if (tidp) {
!         lock_ObtainMutex(&tidp->mx);
!         tidp->flags |= SMB_TIDFLAG_DELETE;
!         lock_ReleaseMutex(&tidp->mx);
!         smb_ReleaseTID(tidp);
!     }
  
!     return 0;
  }
  
  long smb_ReceiveCoreOpen(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
!     smb_fid_t *fidp;
      char *pathp;
!     char *lastNamep;
      int share;
      int attribute;
!     long code = 0;
      cm_user_t *userp;
      cm_scache_t *scp;
      time_t dosTime;
      int caseFold;
!     cm_space_t *spacep;
!     char *tidPathp;
!     cm_req_t req;
  
!     cm_InitReq(&req);
  
      pathp = smb_GetSMBData(inp, NULL);
      pathp = smb_ParseASCIIBlock(pathp, NULL);
***************
*** 4086,4121 ****
      }
  #endif
  
! 	share = smb_GetSMBParm(inp, 0);
      attribute = smb_GetSMBParm(inp, 1);
  
! 	spacep = inp->spacep;
! 	smb_StripLastComponent(spacep->data, &lastNamep, pathp);
! 	if (lastNamep && strcmp(lastNamep, SMB_IOCTL_FILENAME) == 0) {
! 		/* special case magic file name for receiving IOCTL requests
           * (since IOCTL calls themselves aren't getting through).
           */
          fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
! 		smb_SetupIoctlFid(fidp, spacep);
! 		smb_SetSMBParm(outp, 0, fidp->fid);
          smb_SetSMBParm(outp, 1, 0);	/* attrs */
          smb_SetSMBParm(outp, 2, 0);	/* next 2 are DOS time */
          smb_SetSMBParm(outp, 3, 0);
          smb_SetSMBParm(outp, 4, 0);	/* next 2 are length */
          smb_SetSMBParm(outp, 5, 0x7fff);
! 		/* pass the open mode back */
          smb_SetSMBParm(outp, 6, (share & 0xf));
          smb_SetSMBDataLength(outp, 0);
          smb_ReleaseFID(fidp);
          return 0;
      }
  
! 	userp = smb_GetUser(vcp, inp);
  
! 	caseFold = CM_FLAG_CASEFOLD;
  
! 	code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!     if(code) {
          cm_ReleaseUser(userp);
          return CM_ERROR_NOSUCHPATH;
      }
--- 4115,4150 ----
      }
  #endif
  
!     share = smb_GetSMBParm(inp, 0);
      attribute = smb_GetSMBParm(inp, 1);
  
!     spacep = inp->spacep;
!     smb_StripLastComponent(spacep->data, &lastNamep, pathp);
!     if (lastNamep && strcmp(lastNamep, SMB_IOCTL_FILENAME) == 0) {
!         /* special case magic file name for receiving IOCTL requests
           * (since IOCTL calls themselves aren't getting through).
           */
          fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
!         smb_SetupIoctlFid(fidp, spacep);
!         smb_SetSMBParm(outp, 0, fidp->fid);
          smb_SetSMBParm(outp, 1, 0);	/* attrs */
          smb_SetSMBParm(outp, 2, 0);	/* next 2 are DOS time */
          smb_SetSMBParm(outp, 3, 0);
          smb_SetSMBParm(outp, 4, 0);	/* next 2 are length */
          smb_SetSMBParm(outp, 5, 0x7fff);
!         /* pass the open mode back */
          smb_SetSMBParm(outp, 6, (share & 0xf));
          smb_SetSMBDataLength(outp, 0);
          smb_ReleaseFID(fidp);
          return 0;
      }
  
!     userp = smb_GetUser(vcp, inp);
  
!     caseFold = CM_FLAG_CASEFOLD;
  
!     code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!     if (code) {
          cm_ReleaseUser(userp);
          return CM_ERROR_NOSUCHPATH;
      }
***************
*** 4124,4145 ****
          
      if (code) {
          cm_ReleaseUser(userp);
! 		return code;
! 	}
!         
      code = cm_CheckOpen(scp, share & 0x7, 0, userp, &req);
! 	if (code) {
! 		cm_ReleaseSCache(scp);
! 		cm_ReleaseUser(userp);
! 		return code;
! 	}
! 
! 	/* don't need callback to check file type, since file types never
! 	 * change, and namei and cm_Lookup all stat the object at least once on
! 	 * a successful return.
       */
      if (scp->fileType != CM_SCACHETYPE_FILE) {
! 		cm_ReleaseSCache(scp);
          cm_ReleaseUser(userp);
          return CM_ERROR_ISDIR;
      }
--- 4153,4174 ----
          
      if (code) {
          cm_ReleaseUser(userp);
!         return code;
!     }
! 
      code = cm_CheckOpen(scp, share & 0x7, 0, userp, &req);
!     if (code) {
!         cm_ReleaseSCache(scp);
!         cm_ReleaseUser(userp);
!         return code;
!     }
! 
!     /* don't need callback to check file type, since file types never
!      * change, and namei and cm_Lookup all stat the object at least once on
!      * a successful return.
       */
      if (scp->fileType != CM_SCACHETYPE_FILE) {
!         cm_ReleaseSCache(scp);
          cm_ReleaseUser(userp);
          return CM_ERROR_ISDIR;
      }
***************
*** 4147,4179 ****
      fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
      osi_assert(fidp);
  
! 	/* save a pointer to the vnode */
      fidp->scp = scp;
  
      if ((share & 0xf) == 0)
          fidp->flags |= SMB_FID_OPENREAD;
! 	else if ((share & 0xf) == 1)
          fidp->flags |= SMB_FID_OPENWRITE;
! 	else 
          fidp->flags |= (SMB_FID_OPENREAD | SMB_FID_OPENWRITE);
  
! 	lock_ObtainMutex(&scp->mx);
! 	smb_SetSMBParm(outp, 0, fidp->fid);
      smb_SetSMBParm(outp, 1, smb_Attributes(scp));
! 	smb_DosUTimeFromUnixTime(&dosTime, scp->clientModTime);
      smb_SetSMBParm(outp, 2, dosTime & 0xffff);
      smb_SetSMBParm(outp, 3, (dosTime >> 16) & 0xffff);
      smb_SetSMBParm(outp, 4, scp->length.LowPart & 0xffff);
      smb_SetSMBParm(outp, 5, (scp->length.LowPart >> 16) & 0xffff);
! 	/* pass the open mode back; XXXX add access checks */
      smb_SetSMBParm(outp, 6, (share & 0xf));
      smb_SetSMBDataLength(outp, 0);
! 	lock_ReleaseMutex(&scp->mx);
          
! 	/* notify open */
      cm_Open(scp, 0, userp);
  
! 	/* send and free packet */
      smb_ReleaseFID(fidp);
      cm_ReleaseUser(userp);
      /* don't release scp, since we've squirreled away the pointer in the fid struct */
--- 4176,4208 ----
      fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
      osi_assert(fidp);
  
!     /* save a pointer to the vnode */
      fidp->scp = scp;
  
      if ((share & 0xf) == 0)
          fidp->flags |= SMB_FID_OPENREAD;
!     else if ((share & 0xf) == 1)
          fidp->flags |= SMB_FID_OPENWRITE;
!     else 
          fidp->flags |= (SMB_FID_OPENREAD | SMB_FID_OPENWRITE);
  
!     lock_ObtainMutex(&scp->mx);
!     smb_SetSMBParm(outp, 0, fidp->fid);
      smb_SetSMBParm(outp, 1, smb_Attributes(scp));
!     smb_DosUTimeFromUnixTime(&dosTime, scp->clientModTime);
      smb_SetSMBParm(outp, 2, dosTime & 0xffff);
      smb_SetSMBParm(outp, 3, (dosTime >> 16) & 0xffff);
      smb_SetSMBParm(outp, 4, scp->length.LowPart & 0xffff);
      smb_SetSMBParm(outp, 5, (scp->length.LowPart >> 16) & 0xffff);
!     /* pass the open mode back; XXXX add access checks */
      smb_SetSMBParm(outp, 6, (share & 0xf));
      smb_SetSMBDataLength(outp, 0);
!     lock_ReleaseMutex(&scp->mx);
          
!     /* notify open */
      cm_Open(scp, 0, userp);
  
!     /* send and free packet */
      smb_ReleaseFID(fidp);
      cm_ReleaseUser(userp);
      /* don't release scp, since we've squirreled away the pointer in the fid struct */
***************
*** 4181,4302 ****
  }
  
  typedef struct smb_unlinkRock {
! 	cm_scache_t *dscp;
! 	cm_user_t *userp;
! 	cm_req_t *reqp;
! 	smb_vc_t *vcp;
! 	char *maskp;		/* pointer to the star pattern */
! 	int flags;
! 	int any;
  } smb_unlinkRock_t;
  
  int smb_UnlinkProc(cm_scache_t *dscp, cm_dirEntry_t *dep, void *vrockp, osi_hyper_t *offp)
  {
! 	long code = 0;
! 	smb_unlinkRock_t *rockp;
! 	int caseFold;
! 	int match;
! 	char shortName[13];
! 	char *matchName;
          
! 	rockp = vrockp;
  
      caseFold = ((rockp->flags & SMB_MASKFLAG_CASEFOLD)? CM_FLAG_CASEFOLD : 0);
      if (!(rockp->vcp->flags & SMB_VCFLAG_USEV3))
          caseFold |= CM_FLAG_8DOT3;
  
! 	matchName = dep->name;
! 	match = smb_V3MatchMask(matchName, rockp->maskp, caseFold);
! 	if (!match
! 	    && (rockp->flags & SMB_MASKFLAG_TILDE)
! 	    && !cm_Is8Dot3(dep->name)) {
! 		cm_Gen8Dot3Name(dep, shortName, NULL);
! 		matchName = shortName;
          /* 8.3 matches are always case insensitive */
          match = smb_V3MatchMask(matchName, rockp->maskp, caseFold | CM_FLAG_CASEFOLD);
! 	}
! 	if (match) {
! 		osi_Log1(smb_logp, "Unlinking %s",
! 				 osi_LogSaveString(smb_logp, matchName));
! 		code = cm_Unlink(dscp, dep->name, rockp->userp, rockp->reqp);
! 		if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
! 			smb_NotifyChange(FILE_ACTION_REMOVED,
! 							 FILE_NOTIFY_CHANGE_FILE_NAME,
! 							 dscp, dep->name, NULL, TRUE);
! 		if (code == 0) {
! 			rockp->any = 1;
              /* If we made a case sensitive exact match, we might as well quit now. */
!             if(!(rockp->flags & SMB_MASKFLAG_CASEFOLD) && !strcmp(matchName, rockp->maskp))
                  code = CM_ERROR_STOPNOW;
          }
! 	}
! 	else code = 0;
  
! 	return code;
  }
  
  long smb_ReceiveCoreUnlink(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
! 	int attribute;
! 	long code = 0;
! 	char *pathp;
! 	char *tp;
! 	cm_space_t *spacep;
! 	cm_scache_t *dscp;
! 	char *lastNamep;
! 	smb_unlinkRock_t rock;
! 	cm_user_t *userp;
! 	osi_hyper_t thyper;
! 	int caseFold;
! 	char *tidPathp;
! 	cm_req_t req;
! 
! 	cm_InitReq(&req);
! 
! 	attribute = smb_GetSMBParm(inp, 0);
!         
! 	tp = smb_GetSMBData(inp, NULL);
! 	pathp = smb_ParseASCIIBlock(tp, &tp);
! 
! 	osi_Log1(smb_logp, "SMB receive unlink %s",
! 			 osi_LogSaveString(smb_logp, pathp));
  
! 	spacep = inp->spacep;
! 	smb_StripLastComponent(spacep->data, &lastNamep, pathp);
  
! 	userp = smb_GetUser(vcp, inp);
  
! 	caseFold = CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD;
  
! 	code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!     if(code) {
          cm_ReleaseUser(userp);
          return CM_ERROR_NOSUCHPATH;
      }
! 	code = cm_NameI(cm_rootSCachep, spacep->data, caseFold, userp, tidPathp,
! 					&req, &dscp);
  
! 	if (code) {
! 		cm_ReleaseUser(userp);
! 		return code;
! 	}
!         
! 	/* otherwise, scp points to the parent directory. */
! 	if (!lastNamep) 
! 		lastNamep = pathp;
! 	else 
! 		lastNamep++;
! 
! 	rock.any = 0;
! 	rock.maskp = smb_FindMask(pathp);
! 	rock.flags = ((strchr(rock.maskp, '~') != NULL) ? SMB_MASKFLAG_TILDE : 0);
!         
! 	thyper.LowPart = 0;
! 	thyper.HighPart = 0;
! 	rock.userp = userp;
! 	rock.reqp = &req;
! 	rock.dscp = dscp;
! 	rock.vcp = vcp;
  
      /* Now, if we aren't dealing with a wildcard match, we first try an exact 
       * match.  If that fails, we do a case insensitve match. 
--- 4210,4332 ----
  }
  
  typedef struct smb_unlinkRock {
!     cm_scache_t *dscp;
!     cm_user_t *userp;
!     cm_req_t *reqp;
!     smb_vc_t *vcp;
!     char *maskp;		/* pointer to the star pattern */
!     int flags;
!     int any;
  } smb_unlinkRock_t;
  
  int smb_UnlinkProc(cm_scache_t *dscp, cm_dirEntry_t *dep, void *vrockp, osi_hyper_t *offp)
  {
!     long code = 0;
!     smb_unlinkRock_t *rockp;
!     int caseFold;
!     int match;
!     char shortName[13];
!     char *matchName;
          
!     rockp = vrockp;
  
      caseFold = ((rockp->flags & SMB_MASKFLAG_CASEFOLD)? CM_FLAG_CASEFOLD : 0);
      if (!(rockp->vcp->flags & SMB_VCFLAG_USEV3))
          caseFold |= CM_FLAG_8DOT3;
  
!     matchName = dep->name;
!     match = smb_V3MatchMask(matchName, rockp->maskp, caseFold);
!     if (!match &&
!          (rockp->flags & SMB_MASKFLAG_TILDE) &&
!          !cm_Is8Dot3(dep->name)) {
!         cm_Gen8Dot3Name(dep, shortName, NULL);
!         matchName = shortName;
          /* 8.3 matches are always case insensitive */
          match = smb_V3MatchMask(matchName, rockp->maskp, caseFold | CM_FLAG_CASEFOLD);
!     }
!     if (match) {
!         osi_Log1(smb_logp, "Unlinking %s",
!                  osi_LogSaveString(smb_logp, matchName));
!         code = cm_Unlink(dscp, dep->name, rockp->userp, rockp->reqp);
!         if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
!             smb_NotifyChange(FILE_ACTION_REMOVED,
!                              FILE_NOTIFY_CHANGE_FILE_NAME,
!                              dscp, dep->name, NULL, TRUE);
!         if (code == 0) {
!             rockp->any = 1;
! 
              /* If we made a case sensitive exact match, we might as well quit now. */
!             if (!(rockp->flags & SMB_MASKFLAG_CASEFOLD) && !strcmp(matchName, rockp->maskp))
                  code = CM_ERROR_STOPNOW;
          }
!     }
!     else code = 0;
  
!     return code;
  }
  
  long smb_ReceiveCoreUnlink(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
!     int attribute;
!     long code = 0;
!     char *pathp;
!     char *tp;
!     cm_space_t *spacep;
!     cm_scache_t *dscp;
!     char *lastNamep;
!     smb_unlinkRock_t rock;
!     cm_user_t *userp;
!     osi_hyper_t thyper;
!     int caseFold;
!     char *tidPathp;
!     cm_req_t req;
  
!     cm_InitReq(&req);
  
!     attribute = smb_GetSMBParm(inp, 0);
!         
!     tp = smb_GetSMBData(inp, NULL);
!     pathp = smb_ParseASCIIBlock(tp, &tp);
  
!     osi_Log1(smb_logp, "SMB receive unlink %s",
!              osi_LogSaveString(smb_logp, pathp));
  
!     spacep = inp->spacep;
!     smb_StripLastComponent(spacep->data, &lastNamep, pathp);
! 
!     userp = smb_GetUser(vcp, inp);
! 
!     caseFold = CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD;
! 
!     code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!     if (code) {
          cm_ReleaseUser(userp);
          return CM_ERROR_NOSUCHPATH;
      }
!     code = cm_NameI(cm_rootSCachep, spacep->data, caseFold, userp, tidPathp,
!                     &req, &dscp);
! 
!     if (code) {
!         cm_ReleaseUser(userp);
!         return code;
!     }
!         
!     /* otherwise, scp points to the parent directory. */
!     if (!lastNamep) 
!         lastNamep = pathp;
!     else 
!         lastNamep++;
  
!     rock.any = 0;
!     rock.maskp = smb_FindMask(pathp);
!     rock.flags = ((strchr(rock.maskp, '~') != NULL) ? SMB_MASKFLAG_TILDE : 0);
! 
!     thyper.LowPart = 0;
!     thyper.HighPart = 0;
!     rock.userp = userp;
!     rock.reqp = &req;
!     rock.dscp = dscp;
!     rock.vcp = vcp;
  
      /* Now, if we aren't dealing with a wildcard match, we first try an exact 
       * match.  If that fails, we do a case insensitve match. 
***************
*** 4304,4310 ****
      if (!(rock.flags & SMB_MASKFLAG_TILDE) &&
          !smb_IsStarMask(rock.maskp)) {
          code = cm_ApplyDir(dscp, smb_UnlinkProc, &rock, &thyper, userp, &req, NULL);
!         if(!rock.any) {
              thyper.LowPart = 0;
              thyper.HighPart = 0;
              rock.flags |= SMB_MASKFLAG_CASEFOLD;
--- 4334,4340 ----
      if (!(rock.flags & SMB_MASKFLAG_TILDE) &&
          !smb_IsStarMask(rock.maskp)) {
          code = cm_ApplyDir(dscp, smb_UnlinkProc, &rock, &thyper, userp, &req, NULL);
!         if (!rock.any) {
              thyper.LowPart = 0;
              thyper.HighPart = 0;
              rock.flags |= SMB_MASKFLAG_CASEFOLD;
***************
*** 4317,4484 ****
      if (code == CM_ERROR_STOPNOW) 
          code = 0;
  
! 	cm_ReleaseUser(userp);
          
! 	cm_ReleaseSCache(dscp);
  
! 	if (code == 0 && !rock.any)
! 		code = CM_ERROR_NOSUCHFILE;
! 	return code;
! }
  
  typedef struct smb_renameRock {
! 	cm_scache_t *odscp;	/* old dir */
! 	cm_scache_t *ndscp;	/* new dir */
! 	cm_user_t *userp;	/* user */
! 	cm_req_t *reqp;		/* request struct */
! 	smb_vc_t *vcp;		/* virtual circuit */
! 	char *maskp;		/* pointer to star pattern of old file name */
! 	int flags;		    /* tilde, casefold, etc */
! 	char *newNamep;		/* ptr to the new file's name */
  } smb_renameRock_t;
  
  int smb_RenameProc(cm_scache_t *dscp, cm_dirEntry_t *dep, void *vrockp, osi_hyper_t *offp)
  {
! 	long code = 0;
! 	smb_renameRock_t *rockp;
! 	int caseFold;
! 	int match;
! 	char shortName[13];
!         
! 	rockp = (smb_renameRock_t *) vrockp;
  
      caseFold = ((rockp->flags & SMB_MASKFLAG_CASEFOLD)? CM_FLAG_CASEFOLD : 0);
      if (!(rockp->vcp->flags & SMB_VCFLAG_USEV3))
          caseFold |= CM_FLAG_8DOT3;
  
! 	match = smb_V3MatchMask(dep->name, rockp->maskp, caseFold);
! 	if (!match
! 	    && (rockp->flags & SMB_MASKFLAG_TILDE)
! 	    && !cm_Is8Dot3(dep->name)) {
! 		cm_Gen8Dot3Name(dep, shortName, NULL);
! 		match = smb_V3MatchMask(shortName, rockp->maskp, caseFold);
! 	}
! 	if (match) {
! 		code = cm_Rename(rockp->odscp, dep->name,
! 						 rockp->ndscp, rockp->newNamep, rockp->userp,
! 						 rockp->reqp);	
! 		/* if the call worked, stop doing the search now, since we
! 		 * really only want to rename one file.
! 		 */
! 		if (code == 0) 
! 			code = CM_ERROR_STOPNOW;
! 	}
! 	else code = 0;
! 
! 	return code;
! }
! 
! long smb_ReceiveCoreRename(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
! {
! 	long code = 0;
! 	char *oldPathp;
! 	char *newPathp;
! 	char *tp;
! 	cm_space_t *spacep = NULL;
! 	smb_renameRock_t rock;
! 	cm_scache_t *oldDscp = NULL;
! 	cm_scache_t *newDscp = NULL;
! 	cm_scache_t *tmpscp= NULL;
! 	cm_scache_t *tmpscp2 = NULL;
! 	char *oldLastNamep;
! 	char *newLastNamep;
! 	osi_hyper_t thyper;
! 	cm_user_t *userp;
! 	int caseFold;
! 	char *tidPathp;
! 	DWORD filter;
! 	cm_req_t req;
! 
! 	cm_InitReq(&req);
!         
! 	tp = smb_GetSMBData(inp, NULL);
! 	oldPathp = smb_ParseASCIIBlock(tp, &tp);
! 	newPathp = smb_ParseASCIIBlock(tp, &tp);
! 
! 	osi_Log2(smb_logp, "smb rename [%s] to [%s]",
! 			 osi_LogSaveString(smb_logp, oldPathp),
! 			 osi_LogSaveString(smb_logp, newPathp));
! 
! 	spacep = inp->spacep;
! 	smb_StripLastComponent(spacep->data, &oldLastNamep, oldPathp);
! 
! 	userp = smb_GetUser(vcp, inp);
! 
!  /*
!   * Changed to use CASEFOLD always.  This enables us to rename Foo/baz when
!   * what actually exists is foo/baz.  I don't know why the code used to be
!   * the way it was.  1/29/96
!   *
!   *     	caseFold = ((vcp->flags & SMB_VCFLAG_USEV3) ? 0: CM_FLAG_CASEFOLD);
!   *
!   * Changed to use CM_FLAG_FOLLOW.  7/24/96
!   *
!   *	caseFold = CM_FLAG_CASEFOLD;
!   */
! 	caseFold = CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD;
  
! 	code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!     if(code) {
          cm_ReleaseUser(userp);
          return CM_ERROR_NOSUCHPATH;
      }
- 	code = cm_NameI(cm_rootSCachep, spacep->data, caseFold,
- 					userp, tidPathp, &req, &oldDscp);
  
! 	if (code) {
! 		cm_ReleaseUser(userp);
! 		return code;
! 	}
!         
! 	smb_StripLastComponent(spacep->data, &newLastNamep, newPathp);
! 	code = cm_NameI(cm_rootSCachep, spacep->data, caseFold,
! 					userp, tidPathp, &req, &newDscp);
! 
! 	if (code) {
! 		cm_ReleaseSCache(oldDscp);
! 		cm_ReleaseUser(userp);
! 		return code;
! 	}
!         
! 	/* otherwise, oldDscp and newDscp point to the corresponding directories.
! 	 * next, get the component names, and lower case them.
! 	 */
! 
! 	/* handle the old name first */
! 	if (!oldLastNamep) 
! 		oldLastNamep = oldPathp;
! 	else 
! 		oldLastNamep++;
! 
! 	/* and handle the new name, too */
! 	if (!newLastNamep) 
! 		newLastNamep = newPathp;
! 	else 
! 		newLastNamep++;
  
      /* TODO: The old name could be a wildcard.  The new name must not be */
! 	
! 	/* do the vnode call */
! 	rock.odscp = oldDscp;
! 	rock.ndscp = newDscp;
! 	rock.userp = userp;
! 	rock.reqp = &req;
! 	rock.vcp = vcp;
! 	rock.maskp = oldLastNamep;
! 	rock.flags = ((strchr(oldLastNamep, '~') != NULL) ? SMB_MASKFLAG_TILDE : 0);
! 	rock.newNamep = newLastNamep;
  
      /* Check if the file already exists; if so return error */
! 	code = cm_Lookup(newDscp,newLastNamep,CM_FLAG_CHECKPATH,userp,&req,&tmpscp);
! 	if ((code != CM_ERROR_NOSUCHFILE) && (code != CM_ERROR_NOSUCHPATH) && (code != CM_ERROR_NOSUCHVOLUME) ) {
          osi_Log2(smb_logp, "  lookup returns %ld for [%s]", code,
                   osi_LogSaveString(afsd_logp, newLastNamep));
!  
          /* Check if the old and the new names differ only in case. If so return
           * success, else return CM_ERROR_EXISTS 
           */
--- 4347,4503 ----
      if (code == CM_ERROR_STOPNOW) 
          code = 0;
  
!     cm_ReleaseUser(userp);
          
!     cm_ReleaseSCache(dscp);
  
!     if (code == 0 && !rock.any)
!         code = CM_ERROR_NOSUCHFILE;
!     return code;
! }       
  
  typedef struct smb_renameRock {
!     cm_scache_t *odscp;	/* old dir */
!     cm_scache_t *ndscp;	/* new dir */
!     cm_user_t *userp;	/* user */
!     cm_req_t *reqp;		/* request struct */
!     smb_vc_t *vcp;		/* virtual circuit */
!     char *maskp;		/* pointer to star pattern of old file name */
!     int flags;		    /* tilde, casefold, etc */
!     char *newNamep;		/* ptr to the new file's name */
  } smb_renameRock_t;
  
  int smb_RenameProc(cm_scache_t *dscp, cm_dirEntry_t *dep, void *vrockp, osi_hyper_t *offp)
  {
!     long code = 0;
!     smb_renameRock_t *rockp;
!     int caseFold;
!     int match;
!     char shortName[13];
! 
!     rockp = (smb_renameRock_t *) vrockp;
  
      caseFold = ((rockp->flags & SMB_MASKFLAG_CASEFOLD)? CM_FLAG_CASEFOLD : 0);
      if (!(rockp->vcp->flags & SMB_VCFLAG_USEV3))
          caseFold |= CM_FLAG_8DOT3;
  
!     match = smb_V3MatchMask(dep->name, rockp->maskp, caseFold);
!     if (!match &&
!         (rockp->flags & SMB_MASKFLAG_TILDE) &&
!          !cm_Is8Dot3(dep->name)) {
!         cm_Gen8Dot3Name(dep, shortName, NULL);
!         match = smb_V3MatchMask(shortName, rockp->maskp, caseFold);
!     }
!     if (match) {
!         code = cm_Rename(rockp->odscp, dep->name,
!                          rockp->ndscp, rockp->newNamep, rockp->userp,
!                          rockp->reqp);	
!         /* if the call worked, stop doing the search now, since we
!          * really only want to rename one file.
!          */
!         if (code == 0) 
!             code = CM_ERROR_STOPNOW;
!     }       
!     else code = 0;
! 
!     return code;
! }
! 
  
! long 
! smb_Rename(smb_vc_t *vcp, smb_packet_t *inp, char * oldPathp, char * newPathp, int attrs)
! {
!     long code = 0;
!     cm_space_t *spacep = NULL;
!     smb_renameRock_t rock;
!     cm_scache_t *oldDscp = NULL;
!     cm_scache_t *newDscp = NULL;
!     cm_scache_t *tmpscp= NULL;
!     cm_scache_t *tmpscp2 = NULL;
!     char *oldLastNamep;
!     char *newLastNamep;
!     osi_hyper_t thyper;
!     cm_user_t *userp;
!     int caseFold;
!     char *tidPathp;
!     DWORD filter;
!     cm_req_t req;
! 
!     userp = smb_GetUser(vcp, inp);
!     code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!     if (code) {
          cm_ReleaseUser(userp);
          return CM_ERROR_NOSUCHPATH;
      }
  
!     cm_InitReq(&req);
!     spacep = inp->spacep;
!     smb_StripLastComponent(spacep->data, &oldLastNamep, oldPathp);
! 
!     /*
!      * Changed to use CASEFOLD always.  This enables us to rename Foo/baz when
!      * what actually exists is foo/baz.  I don't know why the code used to be
!      * the way it was.  1/29/96
!      *
!      *     	caseFold = ((vcp->flags & SMB_VCFLAG_USEV3) ? 0: CM_FLAG_CASEFOLD);
!      *
!      * Changed to use CM_FLAG_FOLLOW.  7/24/96
!      *
!      *	caseFold = CM_FLAG_CASEFOLD;
!      */
!     caseFold = CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD;
!     code = cm_NameI(cm_rootSCachep, spacep->data, caseFold,
!                     userp, tidPathp, &req, &oldDscp);
! 
!     if (code) {
!         cm_ReleaseUser(userp);
!         return code;
!     }
!         
!     smb_StripLastComponent(spacep->data, &newLastNamep, newPathp);
!     code = cm_NameI(cm_rootSCachep, spacep->data, caseFold,
!                     userp, tidPathp, &req, &newDscp);
! 
!     if (code) {
!         cm_ReleaseSCache(oldDscp);
!         cm_ReleaseUser(userp);
!         return code;
!     }
!         
!     /* otherwise, oldDscp and newDscp point to the corresponding directories.
!      * next, get the component names, and lower case them.
!      */
! 
!     /* handle the old name first */
!     if (!oldLastNamep) 
!         oldLastNamep = oldPathp;
!     else 
!         oldLastNamep++;
! 
!     /* and handle the new name, too */
!     if (!newLastNamep) 
!         newLastNamep = newPathp;
!     else 
!         newLastNamep++;
  
      /* TODO: The old name could be a wildcard.  The new name must not be */
! 
!     /* do the vnode call */
!     rock.odscp = oldDscp;
!     rock.ndscp = newDscp;
!     rock.userp = userp;
!     rock.reqp = &req;
!     rock.vcp = vcp;
!     rock.maskp = oldLastNamep;
!     rock.flags = ((strchr(oldLastNamep, '~') != NULL) ? SMB_MASKFLAG_TILDE : 0);
!     rock.newNamep = newLastNamep;
  
      /* Check if the file already exists; if so return error */
!     code = cm_Lookup(newDscp,newLastNamep,CM_FLAG_CHECKPATH,userp,&req,&tmpscp);
!     if ((code != CM_ERROR_NOSUCHFILE) && (code != CM_ERROR_NOSUCHPATH) && (code != CM_ERROR_NOSUCHVOLUME) ) {
          osi_Log2(smb_logp, "  lookup returns %ld for [%s]", code,
                   osi_LogSaveString(afsd_logp, newLastNamep));
! 
          /* Check if the old and the new names differ only in case. If so return
           * success, else return CM_ERROR_EXISTS 
           */
***************
*** 4502,4658 ****
              code = CM_ERROR_EXISTS;
          }
  
! 		if(tmpscp != NULL)
              cm_ReleaseSCache(tmpscp);
          cm_ReleaseSCache(newDscp);
          cm_ReleaseSCache(oldDscp);
          cm_ReleaseUser(userp);
! 	    return code; 
! 	}
  
      /* Now search the directory for the pattern, and do the appropriate rename when found */
! 	thyper.LowPart = 0;		/* search dir from here */
      thyper.HighPart = 0;
  
      code = cm_ApplyDir(oldDscp, smb_RenameProc, &rock, &thyper, userp, &req, NULL);
  
      if (code == CM_ERROR_STOPNOW)
! 		code = 0;
! 	else if (code == 0)
! 		code = CM_ERROR_NOSUCHFILE;
! 
! 	/* Handle Change Notification */
! 	/*
! 	 * Being lazy, not distinguishing between files and dirs in this
! 	 * filter, since we'd have to do a lookup.
! 	 */
! 	filter = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME;
! 	if (oldDscp == newDscp) {
! 		if (oldDscp->flags & CM_SCACHEFLAG_ANYWATCH)
! 			smb_NotifyChange(FILE_ACTION_RENAMED_OLD_NAME,
! 							 filter, oldDscp, oldLastNamep,
! 							 newLastNamep, TRUE);
! 	} else {
! 		if (oldDscp->flags & CM_SCACHEFLAG_ANYWATCH)
! 			smb_NotifyChange(FILE_ACTION_RENAMED_OLD_NAME,
! 							 filter, oldDscp, oldLastNamep,
! 							 NULL, TRUE);
! 		if (newDscp->flags & CM_SCACHEFLAG_ANYWATCH)
! 			smb_NotifyChange(FILE_ACTION_RENAMED_NEW_NAME,
! 							 filter, newDscp, newLastNamep,
! 							 NULL, TRUE);
! 	}
  
!     if(tmpscp != NULL) 
          cm_ReleaseSCache(tmpscp);
      cm_ReleaseUser(userp);
! 	cm_ReleaseSCache(oldDscp);
! 	cm_ReleaseSCache(newDscp);
! 	return code;
  }
  
  typedef struct smb_rmdirRock {
! 	cm_scache_t *dscp;
! 	cm_user_t *userp;
! 	cm_req_t *reqp;
! 	char *maskp;		/* pointer to the star pattern */
! 	int flags;
! 	int any;
  } smb_rmdirRock_t;
  
  int smb_RmdirProc(cm_scache_t *dscp, cm_dirEntry_t *dep, void *vrockp, osi_hyper_t *offp)
! {
! 	long code = 0;
! 	smb_rmdirRock_t *rockp;
! 	int match;
! 	char shortName[13];
! 	char *matchName;
          
! 	rockp = (smb_rmdirRock_t *) vrockp;
  
! 	matchName = dep->name;
      if (rockp->flags & SMB_MASKFLAG_CASEFOLD)
          match = (cm_stricmp(matchName, rockp->maskp) == 0);
      else
          match = (strcmp(matchName, rockp->maskp) == 0);
! 	if (!match
! 	    && (rockp->flags & SMB_MASKFLAG_TILDE)
! 	    && !cm_Is8Dot3(dep->name)) {
! 		cm_Gen8Dot3Name(dep, shortName, NULL);
! 		matchName = shortName;
! 		match = (cm_stricmp(matchName, rockp->maskp) == 0);
! 	}
! 	if (match) {
! 		osi_Log1(smb_logp, "Removing directory %s",
! 				 osi_LogSaveString(smb_logp, matchName));
! 		code = cm_RemoveDir(dscp, dep->name, rockp->userp, rockp->reqp);
! 		if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
! 			smb_NotifyChange(FILE_ACTION_REMOVED,
! 							 FILE_NOTIFY_CHANGE_DIR_NAME,
! 							 dscp, dep->name, NULL, TRUE);
! 		if (code == 0)
! 			rockp->any = 1;
! 	}
! 	else code = 0;
  
! 	return code;
  }
  
  long smb_ReceiveCoreRemoveDir(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
! 	long code = 0;
! 	char *pathp;
! 	char *tp;
! 	cm_space_t *spacep;
! 	cm_scache_t *dscp;
! 	char *lastNamep;
! 	smb_rmdirRock_t rock;
! 	cm_user_t *userp;
! 	osi_hyper_t thyper;
! 	int caseFold;
! 	char *tidPathp;
! 	cm_req_t req;
! 
! 	cm_InitReq(&req);
! 
! 	tp = smb_GetSMBData(inp, NULL);
! 	pathp = smb_ParseASCIIBlock(tp, &tp);
  
! 	spacep = inp->spacep;
! 	smb_StripLastComponent(spacep->data, &lastNamep, pathp);
  
! 	userp = smb_GetUser(vcp, inp);
  
! 	caseFold = CM_FLAG_CASEFOLD;
  
! 	code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!     if(code) {
          cm_ReleaseUser(userp);
          return CM_ERROR_NOSUCHPATH;
      }
! 	code = cm_NameI(cm_rootSCachep, spacep->data, caseFold | CM_FLAG_FOLLOW,
! 					userp, tidPathp, &req, &dscp);
  
! 	if (code) {
! 		cm_ReleaseUser(userp);
! 		return code;
! 	}
!         
! 	/* otherwise, scp points to the parent directory. */
! 	if (!lastNamep) 
! 		lastNamep = pathp;
! 	else 
! 		lastNamep++;
  	
! 	rock.any = 0;
! 	rock.maskp = lastNamep;
! 	rock.flags = ((strchr(rock.maskp, '~') != NULL) ? SMB_MASKFLAG_TILDE : 0);
! 
! 	thyper.LowPart = 0;
! 	thyper.HighPart = 0;
! 	rock.userp = userp;
! 	rock.reqp = &req;
! 	rock.dscp = dscp;
      /* First do a case sensitive match, and if that fails, do a case insensitive match */
      code = cm_ApplyDir(dscp, smb_RmdirProc, &rock, &thyper, userp, &req, NULL);
      if (code == 0 && !rock.any) {
--- 4521,4826 ----
              code = CM_ERROR_EXISTS;
          }
  
!         if (tmpscp != NULL)
              cm_ReleaseSCache(tmpscp);
          cm_ReleaseSCache(newDscp);
          cm_ReleaseSCache(oldDscp);
          cm_ReleaseUser(userp);
!         return code; 
!     }
  
      /* Now search the directory for the pattern, and do the appropriate rename when found */
!     thyper.LowPart = 0;		/* search dir from here */
      thyper.HighPart = 0;
  
      code = cm_ApplyDir(oldDscp, smb_RenameProc, &rock, &thyper, userp, &req, NULL);
  
      if (code == CM_ERROR_STOPNOW)
!         code = 0;
!     else if (code == 0)
!         code = CM_ERROR_NOSUCHFILE;
! 
!     /* Handle Change Notification */
!     /*
!     * Being lazy, not distinguishing between files and dirs in this
!     * filter, since we'd have to do a lookup.
!     */
!     filter = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME;
!     if (oldDscp == newDscp) {
!         if (oldDscp->flags & CM_SCACHEFLAG_ANYWATCH)
!             smb_NotifyChange(FILE_ACTION_RENAMED_OLD_NAME,
!                              filter, oldDscp, oldLastNamep,
!                              newLastNamep, TRUE);
!     } else {
!         if (oldDscp->flags & CM_SCACHEFLAG_ANYWATCH)
!             smb_NotifyChange(FILE_ACTION_RENAMED_OLD_NAME,
!                              filter, oldDscp, oldLastNamep,
!                              NULL, TRUE);
!         if (newDscp->flags & CM_SCACHEFLAG_ANYWATCH)
!             smb_NotifyChange(FILE_ACTION_RENAMED_NEW_NAME,
!                              filter, newDscp, newLastNamep,
!                              NULL, TRUE);
!     }
! 
!     if (tmpscp != NULL) 
!         cm_ReleaseSCache(tmpscp);
!     cm_ReleaseUser(userp);
!     cm_ReleaseSCache(oldDscp);
!     cm_ReleaseSCache(newDscp);
!     return code;
! }       
! 
! long 
! smb_Link(smb_vc_t *vcp, smb_packet_t *inp, char * oldPathp, char * newPathp) 
! {
!     long code = 0;
!     cm_space_t *spacep = NULL;
!     cm_scache_t *oldDscp = NULL;
!     cm_scache_t *newDscp = NULL;
!     cm_scache_t *tmpscp= NULL;
!     cm_scache_t *tmpscp2 = NULL;
!     cm_scache_t *sscp = NULL;
!     char *oldLastNamep;
!     char *newLastNamep;
!     cm_user_t *userp;
!     int caseFold;
!     char *tidPathp;
!     DWORD filter;
!     cm_req_t req;
! 
!     userp = smb_GetUser(vcp, inp);
! 
!     code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!     if (code) {
!         cm_ReleaseUser(userp);
!         return CM_ERROR_NOSUCHPATH;
!     }
! 
!     cm_InitReq(&req);
! 
!     caseFold = CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD;
! 
!     spacep = inp->spacep;
!     smb_StripLastComponent(spacep->data, &oldLastNamep, oldPathp);
!     
!     code = cm_NameI(cm_rootSCachep, spacep->data, caseFold,
!                     userp, tidPathp, &req, &oldDscp);
!     if (code) {
!         cm_ReleaseUser(userp);
!         return code;
!     }
!         
!     smb_StripLastComponent(spacep->data, &newLastNamep, newPathp);
!     code = cm_NameI(cm_rootSCachep, spacep->data, caseFold,
!                     userp, tidPathp, &req, &newDscp);
!     if (code) {
!         cm_ReleaseSCache(oldDscp);
!         cm_ReleaseUser(userp);
!         return code;
!     }
! 
!     /* Now, although we did two lookups for the two directories (because the same
!      * directory can be referenced through different paths), we only allow hard links
!      * within the same directory. */
!     if (oldDscp != newDscp) {
!         cm_ReleaseSCache(oldDscp);
!         cm_ReleaseSCache(newDscp);
!         cm_ReleaseUser(userp);
!         return CM_ERROR_CROSSDEVLINK;
!     }
! 
!     /* handle the old name first */
!     if (!oldLastNamep) 
!         oldLastNamep = oldPathp;
!     else 
!         oldLastNamep++;
  
!     /* and handle the new name, too */
!     if (!newLastNamep) 
!         newLastNamep = newPathp;
!     else 
!         newLastNamep++;
! 
!     /* now lookup the old name */
!     osi_Log1(smb_logp,"  looking up [%s]", osi_LogSaveString(smb_logp,oldLastNamep));
!     code = cm_Lookup(oldDscp, oldLastNamep, CM_FLAG_CHECKPATH | CM_FLAG_CASEFOLD, userp, &req, &sscp);
!     if (code) {
!         cm_ReleaseSCache(oldDscp);
!         cm_ReleaseSCache(newDscp);
!         cm_ReleaseUser(userp);
!         return code;
!     }
! 
!     /* Check if the file already exists; if so return error */
!     code = cm_Lookup(newDscp,newLastNamep,CM_FLAG_CHECKPATH,userp,&req,&tmpscp);
!     if ((code != CM_ERROR_NOSUCHFILE) && (code != CM_ERROR_NOSUCHPATH) && (code != CM_ERROR_NOSUCHVOLUME) ) {
!         osi_Log2(smb_logp, "  lookup returns %ld for [%s]", code,
!                  osi_LogSaveString(afsd_logp, newLastNamep));
! 
!         /* if the existing link is to the same file, then we return success */
!         if (!code) {
!             if(sscp == tmpscp) {
!                 code = 0;
!             } else {
!                 osi_Log0(smb_logp, "Can't create hardlink.  Target already exists");
!                 code = CM_ERROR_EXISTS;
!             }
!         }
! 
!         if (tmpscp != NULL)
!             cm_ReleaseSCache(tmpscp);
!         cm_ReleaseSCache(sscp);
!         cm_ReleaseSCache(newDscp);
!         cm_ReleaseSCache(oldDscp);
!         cm_ReleaseUser(userp);
!         return code; 
!     }
! 
!     /* now create the hardlink */
!     osi_Log1(smb_logp,"  Attempting to create new link [%s]", osi_LogSaveString(smb_logp, newLastNamep));
!     code = cm_Link(newDscp, newLastNamep, sscp, 0, userp, &req);
!     osi_Log1(smb_logp,"  Link returns %d", code);
! 
!     /* Handle Change Notification */
!     if (code == 0) {
!         filter = (sscp->fileType == CM_SCACHETYPE_FILE)? FILE_NOTIFY_CHANGE_FILE_NAME : FILE_NOTIFY_CHANGE_DIR_NAME;
!         if (newDscp->flags & CM_SCACHEFLAG_ANYWATCH)
!             smb_NotifyChange(FILE_ACTION_ADDED,
!                              filter, newDscp, newLastNamep,
!                              NULL, TRUE);
!     }
! 
!     if (tmpscp != NULL) 
          cm_ReleaseSCache(tmpscp);
      cm_ReleaseUser(userp);
!     cm_ReleaseSCache(sscp);
!     cm_ReleaseSCache(oldDscp);
!     cm_ReleaseSCache(newDscp);
!     return code;
! }
! 
! long 
! smb_ReceiveCoreRename(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
! {
!     char *oldPathp;
!     char *newPathp;
!     char *tp;
! 
!     tp = smb_GetSMBData(inp, NULL);
!     oldPathp = smb_ParseASCIIBlock(tp, &tp);
!     newPathp = smb_ParseASCIIBlock(tp, &tp);
! 
!     osi_Log2(smb_logp, "smb rename [%s] to [%s]",
!               osi_LogSaveString(smb_logp, oldPathp),
!               osi_LogSaveString(smb_logp, newPathp));
! 
!     return smb_Rename(vcp,inp,oldPathp,newPathp,0);
  }
  
+ 
+ 
  typedef struct smb_rmdirRock {
!     cm_scache_t *dscp;
!     cm_user_t *userp;
!     cm_req_t *reqp;
!     char *maskp;		/* pointer to the star pattern */
!     int flags;
!     int any;
  } smb_rmdirRock_t;
  
  int smb_RmdirProc(cm_scache_t *dscp, cm_dirEntry_t *dep, void *vrockp, osi_hyper_t *offp)
! {       
!     long code = 0;
!     smb_rmdirRock_t *rockp;
!     int match;
!     char shortName[13];
!     char *matchName;
          
!     rockp = (smb_rmdirRock_t *) vrockp;
  
!     matchName = dep->name;
      if (rockp->flags & SMB_MASKFLAG_CASEFOLD)
          match = (cm_stricmp(matchName, rockp->maskp) == 0);
      else
          match = (strcmp(matchName, rockp->maskp) == 0);
!     if (!match &&
!          (rockp->flags & SMB_MASKFLAG_TILDE) &&
!          !cm_Is8Dot3(dep->name)) {
!         cm_Gen8Dot3Name(dep, shortName, NULL);
!         matchName = shortName;
!         match = (cm_stricmp(matchName, rockp->maskp) == 0);
!     }       
!     if (match) {
!         osi_Log1(smb_logp, "Removing directory %s",
!                  osi_LogSaveString(smb_logp, matchName));
!         code = cm_RemoveDir(dscp, dep->name, rockp->userp, rockp->reqp);
!         if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
!             smb_NotifyChange(FILE_ACTION_REMOVED,
!                              FILE_NOTIFY_CHANGE_DIR_NAME,
!                              dscp, dep->name, NULL, TRUE);
!         if (code == 0)
!             rockp->any = 1;
!     }
!     else code = 0;
  
!     return code;
  }
  
  long smb_ReceiveCoreRemoveDir(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
!     long code = 0;
!     char *pathp;
!     char *tp;
!     cm_space_t *spacep;
!     cm_scache_t *dscp;
!     char *lastNamep;
!     smb_rmdirRock_t rock;
!     cm_user_t *userp;
!     osi_hyper_t thyper;
!     int caseFold;
!     char *tidPathp;
!     cm_req_t req;
  
!     cm_InitReq(&req);
  
!     tp = smb_GetSMBData(inp, NULL);
!     pathp = smb_ParseASCIIBlock(tp, &tp);
  
!     spacep = inp->spacep;
!     smb_StripLastComponent(spacep->data, &lastNamep, pathp);
  
!     userp = smb_GetUser(vcp, inp);
! 
!     caseFold = CM_FLAG_CASEFOLD;
! 
!     code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!     if (code) {
          cm_ReleaseUser(userp);
          return CM_ERROR_NOSUCHPATH;
      }
!     code = cm_NameI(cm_rootSCachep, spacep->data, caseFold | CM_FLAG_FOLLOW,
!                     userp, tidPathp, &req, &dscp);
  
!     if (code) {
!         cm_ReleaseUser(userp);
!         return code;
!     }
!         
!     /* otherwise, scp points to the parent directory. */
!     if (!lastNamep) 
!         lastNamep = pathp;
!     else 
!         lastNamep++;
  	
!     rock.any = 0;
!     rock.maskp = lastNamep;
!     rock.flags = ((strchr(rock.maskp, '~') != NULL) ? SMB_MASKFLAG_TILDE : 0);
! 
!     thyper.LowPart = 0;
!     thyper.HighPart = 0;
!     rock.userp = userp;
!     rock.reqp = &req;
!     rock.dscp = dscp;
      /* First do a case sensitive match, and if that fails, do a case insensitive match */
      code = cm_ApplyDir(dscp, smb_RmdirProc, &rock, &thyper, userp, &req, NULL);
      if (code == 0 && !rock.any) {
***************
*** 4662,4691 ****
          code = cm_ApplyDir(dscp, smb_RmdirProc, &rock, &thyper, userp, &req, NULL);
      }
  
! 	cm_ReleaseUser(userp);
          
! 	cm_ReleaseSCache(dscp);
  
! 	if (code == 0 && !rock.any)
! 		code = CM_ERROR_NOSUCHFILE;        
! 	return code;
  }
  
  long smb_ReceiveCoreFlush(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
! 	unsigned short fid;
      smb_fid_t *fidp;
      cm_user_t *userp;
      long code = 0;
      cm_req_t req;
  
! 	cm_InitReq(&req);
  
! 	fid = smb_GetSMBParm(inp, 0);
!         
! 	osi_Log1(smb_logp, "SMB flush fid %d", fid);
  
! 	fid = smb_ChainFID(fid, inp);
      fidp = smb_FindFID(vcp, fid, 0);
      if (!fidp || (fidp->flags & SMB_FID_IOCTL)) {
          if (fidp)
--- 4830,4859 ----
          code = cm_ApplyDir(dscp, smb_RmdirProc, &rock, &thyper, userp, &req, NULL);
      }
  
!     cm_ReleaseUser(userp);
          
!     cm_ReleaseSCache(dscp);
  
!     if (code == 0 && !rock.any)
!         code = CM_ERROR_NOSUCHFILE;        
!     return code;
  }
  
  long smb_ReceiveCoreFlush(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
!     unsigned short fid;
      smb_fid_t *fidp;
      cm_user_t *userp;
      long code = 0;
      cm_req_t req;
  
!     cm_InitReq(&req);
  
!     fid = smb_GetSMBParm(inp, 0);
! 
!     osi_Log1(smb_logp, "SMB flush fid %d", fid);
  
!     fid = smb_ChainFID(fid, inp);
      fidp = smb_FindFID(vcp, fid, 0);
      if (!fidp || (fidp->flags & SMB_FID_IOCTL)) {
          if (fidp)
***************
*** 4698,4705 ****
      lock_ObtainMutex(&fidp->mx);
      if (fidp->flags & SMB_FID_OPENWRITE)
          code = cm_FSync(fidp->scp, userp, &req);
! 	else 
! 		code = 0;
      lock_ReleaseMutex(&fidp->mx);
          
      smb_ReleaseFID(fidp);
--- 4866,4873 ----
      lock_ObtainMutex(&fidp->mx);
      if (fidp->flags & SMB_FID_OPENWRITE)
          code = cm_FSync(fidp->scp, userp, &req);
!     else 
!         code = 0;
      lock_ReleaseMutex(&fidp->mx);
          
      smb_ReleaseFID(fidp);
***************
*** 4710,4846 ****
  }
  
  struct smb_FullNameRock {
! 	char *name;
! 	cm_scache_t *vnode;
! 	char *fullName;
  };
  
  int smb_FullNameProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
! 	osi_hyper_t *offp)
  {
! 	char shortName[13];
! 	struct smb_FullNameRock *vrockp;
  
! 	vrockp = (struct smb_FullNameRock *)rockp;
  
! 	if (!cm_Is8Dot3(dep->name)) {
! 		cm_Gen8Dot3Name(dep, shortName, NULL);
! 
! 		if (cm_stricmp(shortName, vrockp->name) == 0) {
! 			vrockp->fullName = strdup(dep->name);
! 			return CM_ERROR_STOPNOW;
! 		}
! 	}
! 	if (cm_stricmp(dep->name, vrockp->name) == 0
! 	    && ntohl(dep->fid.vnode) == vrockp->vnode->fid.vnode
! 	    && ntohl(dep->fid.unique) == vrockp->vnode->fid.unique) {
! 		vrockp->fullName = strdup(dep->name);
! 		return CM_ERROR_STOPNOW;
! 	}
! 	return 0;
  }
  
  void smb_FullName(cm_scache_t *dscp, cm_scache_t *scp, char *pathp,
! 	char **newPathp, cm_user_t *userp, cm_req_t *reqp)
  {
! 	struct smb_FullNameRock rock;
! 	long code = 0;
  
! 	rock.name = pathp;
! 	rock.vnode = scp;
  
! 	code = cm_ApplyDir(dscp, smb_FullNameProc, &rock, NULL, 
! 				userp, reqp, NULL); 
! 	if (code == CM_ERROR_STOPNOW)
! 		*newPathp = rock.fullName;
! 	else
! 		*newPathp = strdup(pathp);
  }
  
  long smb_ReceiveCoreClose(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
! 	unsigned short fid;
      smb_fid_t *fidp;
      cm_user_t *userp;
! 	long dosTime;
      long code = 0;
! 	cm_req_t req;
  
! 	cm_InitReq(&req);
  
! 	fid = smb_GetSMBParm(inp, 0);
! 	dosTime = smb_GetSMBParm(inp, 1) | (smb_GetSMBParm(inp, 2) << 16);
!         
! 	osi_Log1(smb_logp, "SMB close fid %d", fid);
  
! 	fid = smb_ChainFID(fid, inp);
      fidp = smb_FindFID(vcp, fid, 0);
      if (!fidp) {
          return CM_ERROR_BADFD;
      }
          
! 	userp = smb_GetUser(vcp, inp);
  
      lock_ObtainMutex(&fidp->mx);
  
! 	/* Don't jump the gun on an async raw write */
! 	while (fidp->raw_writers) {
! 		lock_ReleaseMutex(&fidp->mx);
! 		thrd_WaitForSingleObject_Event(fidp->raw_write_event, RAWTIMEOUT);
! 		lock_ObtainMutex(&fidp->mx);
! 	}
! 
! 	fidp->flags |= SMB_FID_DELETE;
!         
! 	/* watch for ioctl closes, and read-only opens */
!     if (fidp->scp != NULL
!         && (fidp->flags & (SMB_FID_OPENWRITE | SMB_FID_DELONCLOSE))
           == SMB_FID_OPENWRITE) {
! 		if (dosTime != 0 && dosTime != -1) {
! 			fidp->scp->mask |= CM_SCACHEMASK_CLIENTMODTIME;
              /* This fixes defect 10958 */
              CompensateForSmbClientLastWriteTimeBugs(&dosTime);
! 			smb_UnixTimeFromDosUTime(&fidp->scp->clientModTime, dosTime);
! 		}
          code = cm_FSync(fidp->scp, userp, &req);
! 	}
! 	else 
          code = 0;
  
! 	if (fidp->flags & SMB_FID_DELONCLOSE) {
! 		cm_scache_t *dscp = fidp->NTopen_dscp;
! 		char *pathp = fidp->NTopen_pathp;
! 		char *fullPathp;
! 
! 		smb_FullName(dscp, fidp->scp, pathp, &fullPathp, userp, &req);
! 		if (fidp->scp->fileType == CM_SCACHETYPE_DIRECTORY) {
! 			code = cm_RemoveDir(dscp, fullPathp, userp, &req);
! 			if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
! 				smb_NotifyChange(FILE_ACTION_REMOVED,
                                   FILE_NOTIFY_CHANGE_DIR_NAME,
                                   dscp, fullPathp, NULL, TRUE);
! 		}
! 		else 
          {
! 			code = cm_Unlink(dscp, fullPathp, userp, &req);
! 			if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
! 				smb_NotifyChange(FILE_ACTION_REMOVED,
                                   FILE_NOTIFY_CHANGE_FILE_NAME,
                                   dscp, fullPathp, NULL, TRUE);
! 		}
! 		free(fullPathp);
! 	}
      lock_ReleaseMutex(&fidp->mx);
  
      if (fidp->flags & SMB_FID_NTOPEN) {
! 		cm_ReleaseSCache(fidp->NTopen_dscp);
! 		free(fidp->NTopen_pathp);
! 	}
! 	if (fidp->NTopen_wholepathp)
! 		free(fidp->NTopen_wholepathp);
      
      smb_ReleaseFID(fidp);
! 	cm_ReleaseUser(userp);
      return code;
  }
  
--- 4878,5014 ----
  }
  
  struct smb_FullNameRock {
!     char *name;
!     cm_scache_t *vnode;
!     char *fullName;
  };
  
  int smb_FullNameProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
!                      osi_hyper_t *offp)
  {
!     char shortName[13];
!     struct smb_FullNameRock *vrockp;
! 
!     vrockp = (struct smb_FullNameRock *)rockp;
  
!     if (!cm_Is8Dot3(dep->name)) {
!         cm_Gen8Dot3Name(dep, shortName, NULL);
  
!         if (cm_stricmp(shortName, vrockp->name) == 0) {
!             vrockp->fullName = strdup(dep->name);
!             return CM_ERROR_STOPNOW;
!         }
!     }
!     if (cm_stricmp(dep->name, vrockp->name) == 0 &&
!         ntohl(dep->fid.vnode) == vrockp->vnode->fid.vnode &&
!         ntohl(dep->fid.unique) == vrockp->vnode->fid.unique) {
!         vrockp->fullName = strdup(dep->name);
!         return CM_ERROR_STOPNOW;
!     }
!     return 0;
  }
  
  void smb_FullName(cm_scache_t *dscp, cm_scache_t *scp, char *pathp,
!                   char **newPathp, cm_user_t *userp, cm_req_t *reqp)
  {
!     struct smb_FullNameRock rock;
!     long code = 0;
  
!     rock.name = pathp;
!     rock.vnode = scp;
  
!     code = cm_ApplyDir(dscp, smb_FullNameProc, &rock, NULL, 
!                        userp, reqp, NULL); 
!     if (code == CM_ERROR_STOPNOW)
!         *newPathp = rock.fullName;
!     else
!         *newPathp = strdup(pathp);
  }
  
  long smb_ReceiveCoreClose(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
!     unsigned short fid;
      smb_fid_t *fidp;
      cm_user_t *userp;
!     long dosTime;
      long code = 0;
!     cm_req_t req;
  
!     cm_InitReq(&req);
  
!     fid = smb_GetSMBParm(inp, 0);
!     dosTime = smb_GetSMBParm(inp, 1) | (smb_GetSMBParm(inp, 2) << 16);
  
!     osi_Log1(smb_logp, "SMB close fid %d", fid);
! 
!     fid = smb_ChainFID(fid, inp);
      fidp = smb_FindFID(vcp, fid, 0);
      if (!fidp) {
          return CM_ERROR_BADFD;
      }
          
!     userp = smb_GetUser(vcp, inp);
  
      lock_ObtainMutex(&fidp->mx);
  
!     /* Don't jump the gun on an async raw write */
!     while (fidp->raw_writers) {
!         lock_ReleaseMutex(&fidp->mx);
!         thrd_WaitForSingleObject_Event(fidp->raw_write_event, RAWTIMEOUT);
!         lock_ObtainMutex(&fidp->mx);
!     }
! 
!     fidp->flags |= SMB_FID_DELETE;
!         
!     /* watch for ioctl closes, and read-only opens */
!     if (fidp->scp != NULL &&
!         (fidp->flags & (SMB_FID_OPENWRITE | SMB_FID_DELONCLOSE))
           == SMB_FID_OPENWRITE) {
!         if (dosTime != 0 && dosTime != -1) {
!             fidp->scp->mask |= CM_SCACHEMASK_CLIENTMODTIME;
              /* This fixes defect 10958 */
              CompensateForSmbClientLastWriteTimeBugs(&dosTime);
!             smb_UnixTimeFromDosUTime(&fidp->scp->clientModTime, dosTime);
!         }
          code = cm_FSync(fidp->scp, userp, &req);
!     }
!     else 
          code = 0;
  
!     if (fidp->flags & SMB_FID_DELONCLOSE) {
!         cm_scache_t *dscp = fidp->NTopen_dscp;
!         char *pathp = fidp->NTopen_pathp;
!         char *fullPathp;
! 
!         smb_FullName(dscp, fidp->scp, pathp, &fullPathp, userp, &req);
!         if (fidp->scp->fileType == CM_SCACHETYPE_DIRECTORY) {
!             code = cm_RemoveDir(dscp, fullPathp, userp, &req);
!             if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
!                 smb_NotifyChange(FILE_ACTION_REMOVED,
                                   FILE_NOTIFY_CHANGE_DIR_NAME,
                                   dscp, fullPathp, NULL, TRUE);
!         }
!         else 
          {
!             code = cm_Unlink(dscp, fullPathp, userp, &req);
!             if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
!                 smb_NotifyChange(FILE_ACTION_REMOVED,
                                   FILE_NOTIFY_CHANGE_FILE_NAME,
                                   dscp, fullPathp, NULL, TRUE);
!         }
!         free(fullPathp);
!     }
      lock_ReleaseMutex(&fidp->mx);
  
      if (fidp->flags & SMB_FID_NTOPEN) {
!         cm_ReleaseSCache(fidp->NTopen_dscp);
!         free(fidp->NTopen_pathp);
!     }
!     if (fidp->NTopen_wholepathp)
!         free(fidp->NTopen_wholepathp);
      
      smb_ReleaseFID(fidp);
!     cm_ReleaseUser(userp);
      return code;
  }
  
***************
*** 4855,5001 ****
  	cm_user_t *userp, long *readp, int dosflag)
  #endif /* !DJGPP */
  {
! 	osi_hyper_t offset;
! 	long code = 0;
! 	cm_scache_t *scp;
! 	cm_buf_t *bufferp;
! 	osi_hyper_t fileLength;
! 	osi_hyper_t thyper;
! 	osi_hyper_t lastByte;
! 	osi_hyper_t bufferOffset;
! 	long bufIndex, nbytes;
! 	int chunk;
! 	int sequential = 0;
! 	cm_req_t req;
! 
! 	cm_InitReq(&req);
! 
! 	bufferp = NULL;
! 	offset = *offsetp;
! 
! 	lock_ObtainMutex(&fidp->mx);
! 	scp = fidp->scp;
! 	lock_ObtainMutex(&scp->mx);
! 
! 	if (offset.HighPart == 0) {
! 		chunk = offset.LowPart >> cm_logChunkSize;
! 		if (chunk != fidp->curr_chunk) {
! 			fidp->prev_chunk = fidp->curr_chunk;
! 			fidp->curr_chunk = chunk;
! 		}
! 		if (fidp->curr_chunk == fidp->prev_chunk + 1)
! 			sequential = 1;
! 	}
! 
! 	/* start by looking up the file's end */
! 	code = cm_SyncOp(scp, NULL, userp, &req, 0,
! 					 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
! 	if (code) goto done;
! 
! 	/* now we have the entry locked, look up the length */
! 	fileLength = scp->length;
! 
! 	/* adjust count down so that it won't go past EOF */
! 	thyper.LowPart = count;
! 	thyper.HighPart = 0;
! 	thyper = LargeIntegerAdd(offset, thyper);	/* where read should end */
! 	lastByte = thyper;
! 	if (LargeIntegerGreaterThan(thyper, fileLength)) {
! 		/* we'd read past EOF, so just stop at fileLength bytes.
! 		 * Start by computing how many bytes remain in the file.
! 		 */
! 		thyper = LargeIntegerSubtract(fileLength, offset);
! 
! 		/* if we are past EOF, read 0 bytes */
! 		if (LargeIntegerLessThanZero(thyper))
! 			count = 0;
! 		else
! 			count = thyper.LowPart;
! 	}
! 
! 	*readp = count;
! 
! 	/* now, copy the data one buffer at a time,
! 	 * until we've filled the request packet
! 	 */
! 	while (1) {
! 		/* if we've copied all the data requested, we're done */
! 		if (count <= 0) break;
!                 
! 		/* otherwise, load up a buffer of data */
! 		thyper.HighPart = offset.HighPart;
! 		thyper.LowPart = offset.LowPart & ~(buf_bufferSize-1);
! 		if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
! 			/* wrong buffer */
! 			if (bufferp) {
! 				buf_Release(bufferp);
! 				bufferp = NULL;
! 			}
! 			lock_ReleaseMutex(&scp->mx);
! 
! 			lock_ObtainRead(&scp->bufCreateLock);
! 			code = buf_Get(scp, &thyper, &bufferp);
! 			lock_ReleaseRead(&scp->bufCreateLock);
! 
! 			lock_ObtainMutex(&scp->mx);
! 			if (code) goto done;
! 			bufferOffset = thyper;
! 
! 			/* now get the data in the cache */
! 			while (1) {
! 				code = cm_SyncOp(scp, bufferp, userp, &req, 0,
! 								  CM_SCACHESYNC_NEEDCALLBACK
! 								  | CM_SCACHESYNC_READ);
! 				if (code) goto done;
                                  
! 				if (cm_HaveBuffer(scp, bufferp, 0)) break;
  
! 				/* otherwise, load the buffer and try again */
! 				code = cm_GetBuffer(scp, bufferp, NULL, userp, &req);
! 				if (code) break;
! 			}
! 			if (code) {
! 				buf_Release(bufferp);
! 				bufferp = NULL;
! 				goto done;
! 			}
! 		}	/* if (wrong buffer) ... */
!                 
! 		/* now we have the right buffer loaded.  Copy out the
! 		 * data from here to the user's buffer.
! 		 */
! 		bufIndex = offset.LowPart & (buf_bufferSize - 1);
! 
! 		/* and figure out how many bytes we want from this buffer */
! 		nbytes = buf_bufferSize - bufIndex;	/* what remains in buffer */
! 		if (nbytes > count) nbytes = count;	/* don't go past EOF */
! 		
! 		/* now copy the data */
  #ifdef DJGPP
! 		if (dosflag)
! 			dosmemput(bufferp->datap + bufIndex, nbytes, (dos_ptr)op);
! 		else
  #endif /* DJGPP */
! 			memcpy(op, bufferp->datap + bufIndex, nbytes);
                  
! 		/* adjust counters, pointers, etc. */
! 		op += nbytes;
! 		count -= nbytes;
! 		thyper.LowPart = nbytes;
! 		thyper.HighPart = 0;
! 		offset = LargeIntegerAdd(thyper, offset);
! 	} /* while 1 */
  
    done:
! 	lock_ReleaseMutex(&scp->mx);
! 	lock_ReleaseMutex(&fidp->mx);
! 	if (bufferp) 
! 		buf_Release(bufferp);
  
! 	if (code == 0 && sequential)
! 		cm_ConsiderPrefetch(scp, &lastByte, userp, &req);
  
! 	return code;
  }
  
  /*
--- 5023,5169 ----
  	cm_user_t *userp, long *readp, int dosflag)
  #endif /* !DJGPP */
  {
!     osi_hyper_t offset;
!     long code = 0;
!     cm_scache_t *scp;
!     cm_buf_t *bufferp;
!     osi_hyper_t fileLength;
!     osi_hyper_t thyper;
!     osi_hyper_t lastByte;
!     osi_hyper_t bufferOffset;
!     long bufIndex, nbytes;
!     int chunk;
!     int sequential = 0;
!     cm_req_t req;
! 
!     cm_InitReq(&req);
! 
!     bufferp = NULL;
!     offset = *offsetp;
! 
!     lock_ObtainMutex(&fidp->mx);
!     scp = fidp->scp;
!     lock_ObtainMutex(&scp->mx);
! 
!     if (offset.HighPart == 0) {
!         chunk = offset.LowPart >> cm_logChunkSize;
!         if (chunk != fidp->curr_chunk) {
!             fidp->prev_chunk = fidp->curr_chunk;
!             fidp->curr_chunk = chunk;
!         }
!         if (fidp->curr_chunk == fidp->prev_chunk + 1)
!             sequential = 1;
!     }       
! 
!     /* start by looking up the file's end */
!     code = cm_SyncOp(scp, NULL, userp, &req, 0,
!                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
!     if (code) goto done;
! 
!     /* now we have the entry locked, look up the length */
!     fileLength = scp->length;
! 
!     /* adjust count down so that it won't go past EOF */
!     thyper.LowPart = count;
!     thyper.HighPart = 0;
!     thyper = LargeIntegerAdd(offset, thyper);	/* where read should end */
!     lastByte = thyper;
!     if (LargeIntegerGreaterThan(thyper, fileLength)) {
!         /* we'd read past EOF, so just stop at fileLength bytes.
!          * Start by computing how many bytes remain in the file.
!          */
!         thyper = LargeIntegerSubtract(fileLength, offset);
! 
!         /* if we are past EOF, read 0 bytes */
!         if (LargeIntegerLessThanZero(thyper))
!             count = 0;
!         else
!             count = thyper.LowPart;
!     }       
! 
!     *readp = count;
! 
!     /* now, copy the data one buffer at a time,
!      * until we've filled the request packet
!      */
!     while (1) {
!         /* if we've copied all the data requested, we're done */
!         if (count <= 0) break;
! 
!         /* otherwise, load up a buffer of data */
!         thyper.HighPart = offset.HighPart;
!         thyper.LowPart = offset.LowPart & ~(buf_bufferSize-1);
!         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
!             /* wrong buffer */
!             if (bufferp) {
!                 buf_Release(bufferp);
!                 bufferp = NULL;
!             }
!             lock_ReleaseMutex(&scp->mx);
! 
!             lock_ObtainRead(&scp->bufCreateLock);
!             code = buf_Get(scp, &thyper, &bufferp);
!             lock_ReleaseRead(&scp->bufCreateLock);
! 
!             lock_ObtainMutex(&scp->mx);
!             if (code) goto done;
!             bufferOffset = thyper;
! 
!             /* now get the data in the cache */
!             while (1) {
!                 code = cm_SyncOp(scp, bufferp, userp, &req, 0,
!                                  CM_SCACHESYNC_NEEDCALLBACK |
!                                  CM_SCACHESYNC_READ);
!                 if (code) goto done;
                                  
!                 if (cm_HaveBuffer(scp, bufferp, 0)) break;
  
!                 /* otherwise, load the buffer and try again */
!                 code = cm_GetBuffer(scp, bufferp, NULL, userp, &req);
!                 if (code) break;
!             }
!             if (code) {
!                 buf_Release(bufferp);
!                 bufferp = NULL;
!                 goto done;
!             }
!         }	/* if (wrong buffer) ... */
! 
!         /* now we have the right buffer loaded.  Copy out the
!          * data from here to the user's buffer.
!          */
!         bufIndex = offset.LowPart & (buf_bufferSize - 1);
! 
!         /* and figure out how many bytes we want from this buffer */
!         nbytes = buf_bufferSize - bufIndex;	/* what remains in buffer */
!         if (nbytes > count) nbytes = count;	/* don't go past EOF */
! 
!         /* now copy the data */
  #ifdef DJGPP
!         if (dosflag)
!             dosmemput(bufferp->datap + bufIndex, nbytes, (dos_ptr)op);
!         else
  #endif /* DJGPP */
!             memcpy(op, bufferp->datap + bufIndex, nbytes);
                  
!         /* adjust counters, pointers, etc. */
!         op += nbytes;
!         count -= nbytes;
!         thyper.LowPart = nbytes;
!         thyper.HighPart = 0;
!         offset = LargeIntegerAdd(thyper, offset);
!     } /* while 1 */
  
    done:
!     lock_ReleaseMutex(&scp->mx);
!     lock_ReleaseMutex(&fidp->mx);
!     if (bufferp) 
!         buf_Release(bufferp);
  
!     if (code == 0 && sequential)
!         cm_ConsiderPrefetch(scp, &lastByte, userp, &req);
  
!     return code;
  }
  
  /*
***************
*** 5009,5261 ****
  	cm_user_t *userp, long *writtenp, int dosflag)
  #endif /* !DJGPP */
  {
! 	osi_hyper_t offset;
! 	long code = 0;
! 	long written = 0;
! 	cm_scache_t *scp;
! 	osi_hyper_t fileLength;	/* file's length at start of write */
! 	osi_hyper_t minLength;	/* don't read past this */
! 	long nbytes;		/* # of bytes to transfer this iteration */
! 	cm_buf_t *bufferp;
! 	osi_hyper_t thyper;		/* hyper tmp variable */
! 	osi_hyper_t bufferOffset;
! 	long bufIndex;			/* index in buffer where our data is */
! 	int doWriteBack;
! 	osi_hyper_t writeBackOffset;	/* offset of region to write back when
! 	* I/O is done */
! 	DWORD filter = 0;
! 	cm_req_t req;
  
      osi_Log3(smb_logp, "smb_WriteData fid %d, off 0x%x, size 0x%x",
                fidp->fid, offsetp->LowPart, count);
  
! 	cm_InitReq(&req);
  
! 	bufferp = NULL;
! 	doWriteBack = 0;
! 	offset = *offsetp;
  
! 	lock_ObtainMutex(&fidp->mx);
! 	scp = fidp->scp;
! 	lock_ObtainMutex(&scp->mx);
  
! 	/* start by looking up the file's end */
!     osi_Log1(smb_logp, "smb_WriteData fid %d calling cm_SyncOp NEEDCALLBACK|SETSTATUS|GETSTATUS",
!               fidp->fid);
! 	code = cm_SyncOp(scp, NULL, userp, &req, 0,
! 					 CM_SCACHESYNC_NEEDCALLBACK
! 					 | CM_SCACHESYNC_SETSTATUS
! 					 | CM_SCACHESYNC_GETSTATUS);
!     osi_Log2(smb_logp, "smb_WriteData fid %d calling cm_SyncOp NEEDCALLBACK|SETSTATUS|GETSTATUS returns %d",
!               fidp->fid,code);
! 	if (code) 
! 		goto done;
!         
! 	/* make sure we have a writable FD */
! 	if (!(fidp->flags & SMB_FID_OPENWRITE)) {
! 		code = CM_ERROR_BADFDOP;
! 		goto done;
! 	}
! 	
! 	/* now we have the entry locked, look up the length */
! 	fileLength = scp->length;
! 	minLength = fileLength;
! 	if (LargeIntegerGreaterThan(minLength, scp->serverLength))
! 		minLength = scp->serverLength;
! 
! 	/* adjust file length if we extend past EOF */
! 	thyper.LowPart = count;
! 	thyper.HighPart = 0;
! 	thyper = LargeIntegerAdd(offset, thyper);	/* where write should end */
! 	if (LargeIntegerGreaterThan(thyper, fileLength)) {
! 		/* we'd write past EOF, so extend the file */
! 		scp->mask |= CM_SCACHEMASK_LENGTH;
! 		scp->length = thyper;
! 		filter |= (FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE);
! 	} else
! 		filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
!         
! 	/* now, if the new position (thyper) and the old (offset) are in
! 	 * different storeback windows, remember to store back the previous
! 	 * storeback window when we're done with the write.
! 	 */
! 	if ((thyper.LowPart & (-cm_chunkSize)) !=
! 		 (offset.LowPart & (-cm_chunkSize))) {
! 		/* they're different */
! 		doWriteBack = 1;
! 		writeBackOffset.HighPart = offset.HighPart;
! 		writeBackOffset.LowPart = offset.LowPart & (-cm_chunkSize);
! 	}
!         
! 	*writtenp = count;
! 
! 	/* now, copy the data one buffer at a time, until we've filled the
! 	 * request packet */
! 	while (1) {
! 		/* if we've copied all the data requested, we're done */
! 		if (count <= 0) break;
! 
! 		/* handle over quota or out of space */
! 		if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
! 			*writtenp = written;
! 			break;
! 		}
!                 
! 		/* otherwise, load up a buffer of data */
! 		thyper.HighPart = offset.HighPart;
! 		thyper.LowPart = offset.LowPart & ~(buf_bufferSize-1);
! 		if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
! 			/* wrong buffer */
! 			if (bufferp) {
! 				lock_ReleaseMutex(&bufferp->mx);
! 				buf_Release(bufferp);
! 				bufferp = NULL;
! 			}	
! 			lock_ReleaseMutex(&scp->mx);
! 
! 			lock_ObtainRead(&scp->bufCreateLock);
! 			code = buf_Get(scp, &thyper, &bufferp);
! 			lock_ReleaseRead(&scp->bufCreateLock);
! 
! 			lock_ObtainMutex(&bufferp->mx);
! 			lock_ObtainMutex(&scp->mx);
! 			if (code) goto done;
  
! 			bufferOffset = thyper;
  
! 			/* now get the data in the cache */
! 			while (1) {
                  osi_Log1(smb_logp, "smb_WriteData fid %d calling cm_SyncOp NEEDCALLBACK|WRITE|BUFLOCKED",
                            fidp->fid);
! 				code = cm_SyncOp(scp, bufferp, userp, &req, 0,
! 								 CM_SCACHESYNC_NEEDCALLBACK
! 								 | CM_SCACHESYNC_WRITE
! 								 | CM_SCACHESYNC_BUFLOCKED);
                  osi_Log2(smb_logp, "smb_WriteData fid %d calling cm_SyncOp NEEDCALLBACK|WRITE|BUFLOCKED returns %d",
                            fidp->fid,code);
! 				if (code) 
! 					goto done;
!                                 
! 				/* If we're overwriting the entire buffer, or
! 				 * if we're writing at or past EOF, mark the
! 				 * buffer as current so we don't call
! 				 * cm_GetBuffer.  This skips the fetch from the
! 				 * server in those cases where we're going to 
! 				 * obliterate all the data in the buffer anyway,
! 				 * or in those cases where there is no useful
! 				 * data at the server to start with.
! 				 *
! 				 * Use minLength instead of scp->length, since
! 				 * the latter has already been updated by this
! 				 * call.
! 				 */
! 				if (LargeIntegerGreaterThanOrEqualTo(
! 					bufferp->offset, minLength)
! 				    || LargeIntegerEqualTo(offset, bufferp->offset)
! 				       && (count >= buf_bufferSize
! 					   || LargeIntegerGreaterThanOrEqualTo(
! 					       LargeIntegerAdd(offset,
! 						   ConvertLongToLargeInteger(count)),
! 					       minLength))) {
! 					if (count < buf_bufferSize
! 					    && bufferp->dataVersion == -1)
! 					    memset(bufferp->datap, 0,
! 						   buf_bufferSize);
! 					bufferp->dataVersion = scp->dataVersion;
! 				}
! 
! 				if (cm_HaveBuffer(scp, bufferp, 1)) break;
! 
! 				/* otherwise, load the buffer and try again */
! 				lock_ReleaseMutex(&bufferp->mx);
! 				code = cm_GetBuffer(scp, bufferp, NULL, userp,
! 									&req);
! 				lock_ReleaseMutex(&scp->mx);
! 				lock_ObtainMutex(&bufferp->mx);
! 				lock_ObtainMutex(&scp->mx);
! 				if (code) break;
! 			}
! 			if (code) {
! 				lock_ReleaseMutex(&bufferp->mx);
! 				buf_Release(bufferp);
! 				bufferp = NULL;
! 				goto done;
! 			}
! 		}	/* if (wrong buffer) ... */
!                 
! 		/* now we have the right buffer loaded.  Copy out the
! 		 * data from here to the user's buffer.
! 		 */
! 		bufIndex = offset.LowPart & (buf_bufferSize - 1);
! 
! 		/* and figure out how many bytes we want from this buffer */
! 		nbytes = buf_bufferSize - bufIndex;	/* what remains in buffer */
! 		if (nbytes > count) 
! 			nbytes = count;	/* don't go past end of request */
! 		
! 		/* now copy the data */
  #ifdef DJGPP
! 		if (dosflag)
! 			dosmemget((dos_ptr)op, nbytes, bufferp->datap + bufIndex);
! 		else
  #endif /* DJGPP */
! 			memcpy(bufferp->datap + bufIndex, op, nbytes);
! 		buf_SetDirty(bufferp);
  
! 		/* and record the last writer */
! 		if (bufferp->userp != userp) {
! 			cm_HoldUser(userp);
! 			if (bufferp->userp) 
                  cm_ReleaseUser(bufferp->userp);
! 			bufferp->userp = userp;
! 		}
!                 
! 		/* adjust counters, pointers, etc. */
! 		op += nbytes;
! 		count -= nbytes;
! 		written += nbytes;
! 		thyper.LowPart = nbytes;
! 		thyper.HighPart = 0;
! 		offset = LargeIntegerAdd(thyper, offset);
! 	} /* while 1 */
  
    done:
! 	lock_ReleaseMutex(&scp->mx);
! 	lock_ReleaseMutex(&fidp->mx);
! 	if (bufferp) {
! 		lock_ReleaseMutex(&bufferp->mx);
! 		buf_Release(bufferp);
! 	}
! 
! 	if (code == 0 && filter != 0 && (fidp->flags & SMB_FID_NTOPEN)
! 	    && (fidp->NTopen_dscp->flags & CM_SCACHEFLAG_ANYWATCH)) {
! 		smb_NotifyChange(FILE_ACTION_MODIFIED, filter,
! 						 fidp->NTopen_dscp, fidp->NTopen_pathp,
! 						 NULL, TRUE);
! 	}
  
! 	if (code == 0 && doWriteBack) {
          long code2;
! 		lock_ObtainMutex(&scp->mx);
          osi_Log1(smb_logp, "smb_WriteData fid %d calling cm_SyncOp ASYNCSTORE",
                    fidp->fid);
! 		code2 = cm_SyncOp(scp, NULL, userp, &req, 0, CM_SCACHESYNC_ASYNCSTORE);
          osi_Log2(smb_logp, "smb_WriteData fid %d calling cm_SyncOp ASYNCSTORE returns %d",
                    fidp->fid,code2);
! 		lock_ReleaseMutex(&scp->mx);
! 		cm_QueueBKGRequest(scp, cm_BkgStore, writeBackOffset.LowPart,
! 						   writeBackOffset.HighPart, cm_chunkSize, 0, userp);
! 	}
! 
!     osi_Log2(smb_logp, "smb_WriteData fid %d returns %d",
!               fidp->fid, code);
! 	return code;
  }
  
  long smb_ReceiveCoreWrite(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
! 	osi_hyper_t offset;
!     long count, written = 0;
      unsigned short fd;
      smb_fid_t *fidp;
      long code = 0;
--- 5177,5431 ----
  	cm_user_t *userp, long *writtenp, int dosflag)
  #endif /* !DJGPP */
  {
!     osi_hyper_t offset;
!     long code = 0;
!     long written = 0;
!     cm_scache_t *scp;
!     osi_hyper_t fileLength;	/* file's length at start of write */
!     osi_hyper_t minLength;	/* don't read past this */
!     long nbytes;		/* # of bytes to transfer this iteration */
!     cm_buf_t *bufferp;
!     osi_hyper_t thyper;		/* hyper tmp variable */
!     osi_hyper_t bufferOffset;
!     long bufIndex;		/* index in buffer where our data is */
!     int doWriteBack;
!     osi_hyper_t writeBackOffset;/* offset of region to write back when
!                                  * I/O is done */
!     DWORD filter = 0;
!     cm_req_t req;
  
      osi_Log3(smb_logp, "smb_WriteData fid %d, off 0x%x, size 0x%x",
                fidp->fid, offsetp->LowPart, count);
  
!     *writtenp = 0;
! 
!     cm_InitReq(&req);
! 
!     bufferp = NULL;
!     doWriteBack = 0;
!     offset = *offsetp;
! 
!     lock_ObtainMutex(&fidp->mx);
!     scp = fidp->scp;
!     lock_ObtainMutex(&scp->mx);
! 
!     /* start by looking up the file's end */
!     osi_Log1(smb_logp, "smb_WriteData fid %d calling cm_SyncOp NEEDCALLBACK|SETSTATUS|GETSTATUS",
!               fidp->fid);
!     code = cm_SyncOp(scp, NULL, userp, &req, 0,
!                       CM_SCACHESYNC_NEEDCALLBACK
!                       | CM_SCACHESYNC_SETSTATUS
!                       | CM_SCACHESYNC_GETSTATUS);
!     osi_Log2(smb_logp, "smb_WriteData fid %d calling cm_SyncOp NEEDCALLBACK|SETSTATUS|GETSTATUS returns %d",
!               fidp->fid,code);
!     if (code) 
!         goto done;
!         
!     /* make sure we have a writable FD */
!     if (!(fidp->flags & SMB_FID_OPENWRITE)) {
!         code = CM_ERROR_BADFDOP;
!         goto done;
!     }
! 
!     /* now we have the entry locked, look up the length */
!     fileLength = scp->length;
!     minLength = fileLength;
!     if (LargeIntegerGreaterThan(minLength, scp->serverLength))
!         minLength = scp->serverLength;
  
!     /* adjust file length if we extend past EOF */
!     thyper.LowPart = count;
!     thyper.HighPart = 0;
!     thyper = LargeIntegerAdd(offset, thyper);	/* where write should end */
!     if (LargeIntegerGreaterThan(thyper, fileLength)) {
!         /* we'd write past EOF, so extend the file */
!         scp->mask |= CM_SCACHEMASK_LENGTH;
!         scp->length = thyper;
!         filter |= (FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE);
!     } else
!         filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
! 
!     /* now, if the new position (thyper) and the old (offset) are in
!      * different storeback windows, remember to store back the previous
!      * storeback window when we're done with the write.
!      */
!     if ((thyper.LowPart & (-cm_chunkSize)) !=
!          (offset.LowPart & (-cm_chunkSize))) {
!         /* they're different */
!         doWriteBack = 1;
!         writeBackOffset.HighPart = offset.HighPart;
!         writeBackOffset.LowPart = offset.LowPart & (-cm_chunkSize);
!     }
!         
!     *writtenp = count;
! 
!     /* now, copy the data one buffer at a time, until we've filled the
!      * request packet */
!     while (1) {
!         /* if we've copied all the data requested, we're done */
!         if (count <= 0) 
!             break;
  
!         /* handle over quota or out of space */
!         if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
!             *writtenp = written;
!             code = CM_ERROR_QUOTA;
!             break;
!         }
  
!         /* otherwise, load up a buffer of data */
!         thyper.HighPart = offset.HighPart;
!         thyper.LowPart = offset.LowPart & ~(buf_bufferSize-1);
!         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
!             /* wrong buffer */
!             if (bufferp) {
!                 lock_ReleaseMutex(&bufferp->mx);
!                 buf_Release(bufferp);
!                 bufferp = NULL;
!             }	
!             lock_ReleaseMutex(&scp->mx);
! 
!             lock_ObtainRead(&scp->bufCreateLock);
!             code = buf_Get(scp, &thyper, &bufferp);
!             lock_ReleaseRead(&scp->bufCreateLock);
! 
!             lock_ObtainMutex(&bufferp->mx);
!             lock_ObtainMutex(&scp->mx);
!             if (code) goto done;
  
!             bufferOffset = thyper;
  
!             /* now get the data in the cache */
!             while (1) {
                  osi_Log1(smb_logp, "smb_WriteData fid %d calling cm_SyncOp NEEDCALLBACK|WRITE|BUFLOCKED",
                            fidp->fid);
!                 code = cm_SyncOp(scp, bufferp, userp, &req, 0,
!                                   CM_SCACHESYNC_NEEDCALLBACK
!                                   | CM_SCACHESYNC_WRITE
!                                   | CM_SCACHESYNC_BUFLOCKED);
                  osi_Log2(smb_logp, "smb_WriteData fid %d calling cm_SyncOp NEEDCALLBACK|WRITE|BUFLOCKED returns %d",
                            fidp->fid,code);
!                 if (code) 
!                     goto done;
! 
!                 /* If we're overwriting the entire buffer, or
!                  * if we're writing at or past EOF, mark the
!                  * buffer as current so we don't call
!                  * cm_GetBuffer.  This skips the fetch from the
!                  * server in those cases where we're going to 
!                  * obliterate all the data in the buffer anyway,
!                  * or in those cases where there is no useful
!                  * data at the server to start with.
!                  *
!                  * Use minLength instead of scp->length, since
!                  * the latter has already been updated by this
!                  * call.
!                  */
!                 if (LargeIntegerGreaterThanOrEqualTo(bufferp->offset, minLength)
!                      || LargeIntegerEqualTo(offset, bufferp->offset)
!                      && (count >= buf_bufferSize
!                           || LargeIntegerGreaterThanOrEqualTo(LargeIntegerAdd(offset,
!                                                                                ConvertLongToLargeInteger(count)),
!                                                                minLength))) {
!                     if (count < buf_bufferSize
!                          && bufferp->dataVersion == -1)
!                         memset(bufferp->datap, 0,
!                                 buf_bufferSize);
!                     bufferp->dataVersion = scp->dataVersion;
!                 }
! 
!                 if (cm_HaveBuffer(scp, bufferp, 1)) break;
! 
!                 /* otherwise, load the buffer and try again */
!                 lock_ReleaseMutex(&bufferp->mx);
!                 code = cm_GetBuffer(scp, bufferp, NULL, userp,
!                                      &req);
!                 lock_ReleaseMutex(&scp->mx);
!                 lock_ObtainMutex(&bufferp->mx);
!                 lock_ObtainMutex(&scp->mx);
!                 if (code) break;
!             }
!             if (code) {
!                 lock_ReleaseMutex(&bufferp->mx);
!                 buf_Release(bufferp);
!                 bufferp = NULL;
!                 goto done;
!             }
!         }	/* if (wrong buffer) ... */
! 
!         /* now we have the right buffer loaded.  Copy out the
!          * data from here to the user's buffer.
!          */
!         bufIndex = offset.LowPart & (buf_bufferSize - 1);
! 
!         /* and figure out how many bytes we want from this buffer */
!         nbytes = buf_bufferSize - bufIndex;	/* what remains in buffer */
!         if (nbytes > count) 
!             nbytes = count;	/* don't go past end of request */
! 
!         /* now copy the data */
  #ifdef DJGPP
!         if (dosflag)
!             dosmemget((dos_ptr)op, nbytes, bufferp->datap + bufIndex);
!         else
  #endif /* DJGPP */
!             memcpy(bufferp->datap + bufIndex, op, nbytes);
!         buf_SetDirty(bufferp);
  
!         /* and record the last writer */
!         if (bufferp->userp != userp) {
!             cm_HoldUser(userp);
!             if (bufferp->userp) 
                  cm_ReleaseUser(bufferp->userp);
!             bufferp->userp = userp;
!         }
! 
!         /* adjust counters, pointers, etc. */
!         op += nbytes;
!         count -= nbytes;
!         written += nbytes;
!         thyper.LowPart = nbytes;
!         thyper.HighPart = 0;
!         offset = LargeIntegerAdd(thyper, offset);
!     } /* while 1 */
  
    done:
!     lock_ReleaseMutex(&scp->mx);
!     lock_ReleaseMutex(&fidp->mx);
!     if (bufferp) {
!         lock_ReleaseMutex(&bufferp->mx);
!         buf_Release(bufferp);
!     }
  
!     if (code == 0 && filter != 0 && (fidp->flags & SMB_FID_NTOPEN)
!          && (fidp->NTopen_dscp->flags & CM_SCACHEFLAG_ANYWATCH)) {
!         smb_NotifyChange(FILE_ACTION_MODIFIED, filter,
!                           fidp->NTopen_dscp, fidp->NTopen_pathp,
!                           NULL, TRUE);
!     }       
! 
!     if (code == 0 && doWriteBack) {
          long code2;
!         lock_ObtainMutex(&scp->mx);
          osi_Log1(smb_logp, "smb_WriteData fid %d calling cm_SyncOp ASYNCSTORE",
                    fidp->fid);
!         code2 = cm_SyncOp(scp, NULL, userp, &req, 0, CM_SCACHESYNC_ASYNCSTORE);
          osi_Log2(smb_logp, "smb_WriteData fid %d calling cm_SyncOp ASYNCSTORE returns %d",
                    fidp->fid,code2);
!         lock_ReleaseMutex(&scp->mx);
!         cm_QueueBKGRequest(scp, cm_BkgStore, writeBackOffset.LowPart,
!                             writeBackOffset.HighPart, cm_chunkSize, 0, userp);
!     }
! 
!     osi_Log2(smb_logp, "smb_WriteData fid %d returns %d written %d",
!               fidp->fid, code, *writtenp);
!     return code;
  }
  
  long smb_ReceiveCoreWrite(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
!     osi_hyper_t offset;
!     long count, written = 0, total_written = 0;
      unsigned short fd;
      smb_fid_t *fidp;
      long code = 0;
***************
*** 5270,5536 ****
      offset.LowPart = smb_GetSMBParm(inp, 2) | (smb_GetSMBParm(inp, 3) << 16);
  
      op = smb_GetSMBData(inp, NULL);
! 	op = smb_ParseDataBlock(op, NULL, &inDataBlockCount);
  
      osi_Log3(smb_logp, "smb_ReceiveCoreWrite fid %d, off 0x%x, size 0x%x",
               fd, offset.LowPart, count);
          
! 	fd = smb_ChainFID(fd, inp);
      fidp = smb_FindFID(vcp, fd, 0);
      if (!fidp) {
! 		return CM_ERROR_BADFD;
      }
          
      if (fidp->flags & SMB_FID_IOCTL)
          return smb_IoctlWrite(fidp, vcp, inp, outp);
          
! 	userp = smb_GetUser(vcp, inp);
  
  	/* special case: 0 bytes transferred means truncate to this position */
      if (count == 0) {
! 		cm_req_t req;
  
! 		cm_InitReq(&req);
  
! 		truncAttr.mask = CM_ATTRMASK_LENGTH;
          truncAttr.length.LowPart = offset.LowPart;
          truncAttr.length.HighPart = 0;
! 		lock_ObtainMutex(&fidp->mx);
          code = cm_SetAttr(fidp->scp, &truncAttr, userp, &req);
! 		lock_ReleaseMutex(&fidp->mx);
! 		smb_SetSMBParm(outp, 0, /* count */ 0);
          smb_SetSMBDataLength(outp, 0);
! 		fidp->flags |= SMB_FID_LENGTHSETDONE;
          goto done;
      }
  
! 	/*
! 	 * Work around bug in NT client
! 	 *
! 	 * When copying a file, the NT client should first copy the data,
! 	 * then copy the last write time.  But sometimes the NT client does
! 	 * these in the wrong order, so the data copies would inadvertently
! 	 * cause the last write time to be overwritten.  We try to detect this,
! 	 * and don't set client mod time if we think that would go against the
! 	 * intention.
! 	 */
! 	if ((fidp->flags & SMB_FID_MTIMESETDONE) != SMB_FID_MTIMESETDONE) {
! 		fidp->scp->mask |= CM_SCACHEMASK_CLIENTMODTIME;
! 		fidp->scp->clientModTime = time(NULL);
! 	}
  
  #ifndef DJGPP
  	code = smb_WriteData(fidp, &offset, count, op, userp, &written);
  #else /* DJGPP */
  	code = smb_WriteData(fidp, &offset, count, op, userp, &written, FALSE);
  #endif /* !DJGPP */
! 	if (code == 0 && written < count)
! 		code = CM_ERROR_PARTIALWRITE;
  
! 	/* set the packet data length to 3 bytes for the data block header,
       * plus the size of the data.
       */
! 	smb_SetSMBParm(outp, 0, written);
      smb_SetSMBDataLength(outp, 0);
  
    done:
      smb_ReleaseFID(fidp);
      cm_ReleaseUser(userp);
  
! 	return code;
  }
  
  void smb_CompleteWriteRaw(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp,
! 	NCB *ncbp, raw_write_cont_t *rwcp)
  {
! 	unsigned short fd;
! 	smb_fid_t *fidp;
! 	cm_user_t *userp;
  #ifndef DJGPP
! 	char *rawBuf;
  #else /* DJGPP */
! 	dos_ptr rawBuf;
  #endif /* !DJGPP */
! 	long written = 0;
! 	long code = 0;
  
! 	fd = smb_GetSMBParm(inp, 0);
! 	fidp = smb_FindFID(vcp, fd, 0);
  
! 	osi_Log2(smb_logp, "Completing Raw Write offset %x count %x",
! 	     	 rwcp->offset.LowPart, rwcp->count);
  
! 	userp = smb_GetUser(vcp, inp);
  
  #ifndef DJGPP
! 	rawBuf = rwcp->buf;
! 	code = smb_WriteData(fidp, &rwcp->offset, rwcp->count, rawBuf, userp,
  						 &written);
  #else /* DJGPP */
! 	rawBuf = (dos_ptr) rwcp->buf;
! 	code = smb_WriteData(fidp, &rwcp->offset, rwcp->count,
                           (unsigned char *) rawBuf, userp,
                           &written, TRUE);
  #endif /* !DJGPP */
  
! 	if (rwcp->writeMode & 0x1) {	/* synchronous */
! 		smb_t *op;
  
! 		smb_FormatResponsePacket(vcp, inp, outp);
! 		op = (smb_t *) outp;
! 		op->com = 0x20;		/* SMB_COM_WRITE_COMPLETE */
! 		smb_SetSMBParm(outp, 0, written + rwcp->alreadyWritten);
! 		smb_SetSMBDataLength(outp,  0);
! 		smb_SendPacket(vcp, outp);
! 		smb_FreePacket(outp);
! 	}
! 	else {				/* asynchronous */
! 		lock_ObtainMutex(&fidp->mx);
! 		fidp->raw_writers--;
! 		if (fidp->raw_writers == 0)
! 			thrd_SetEvent(fidp->raw_write_event);
! 		lock_ReleaseMutex(&fidp->mx);
! 	}
  
! 	/* Give back raw buffer */
! 	lock_ObtainMutex(&smb_RawBufLock);
  #ifndef DJGPP
! 	*((char **)rawBuf) = smb_RawBufs;
  #else /* DJGPP */
      _farpokel(_dos_ds, rawBuf, smb_RawBufs);
  #endif /* !DJGPP */
! 	smb_RawBufs = rawBuf;
! 	lock_ReleaseMutex(&smb_RawBufLock);
  
! 	smb_ReleaseFID(fidp);
! 	cm_ReleaseUser(userp);
  }
  
  long smb_ReceiveCoreWriteRawDummy(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
! 	return 0;
  }
  
  long smb_ReceiveCoreWriteRaw(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp, raw_write_cont_t *rwcp)
  {
! 	osi_hyper_t offset;
!     long count, written = 0;
! 	long totalCount;
      unsigned short fd;
      smb_fid_t *fidp;
      long code = 0;
      cm_user_t *userp;
      char *op;
! 	unsigned short writeMode;
  #ifndef DJGPP
! 	char *rawBuf;
  #else /* DJGPP */
      dos_ptr rawBuf;
  #endif /* !DJGPP */
  
      fd = smb_GetSMBParm(inp, 0);
! 	totalCount = smb_GetSMBParm(inp, 1);
      count = smb_GetSMBParm(inp, 10);
      offset.HighPart = 0;	/* too bad */
      offset.LowPart = smb_GetSMBParm(inp, 3) | (smb_GetSMBParm(inp, 4) << 16);
! 	writeMode = smb_GetSMBParm(inp, 7);
  
! 	op = (char *) inp->data;
! 	op += smb_GetSMBParm(inp, 11);
  
      osi_Log4(smb_logp,
               "smb_ReceiveCoreWriteRaw fd %d, off 0x%x, size 0x%x, WriteMode 0x%x",
               fd, offset.LowPart, count, writeMode);
          
! 	fd = smb_ChainFID(fd, inp);
      fidp = smb_FindFID(vcp, fd, 0);
      if (!fidp) {
! 		return CM_ERROR_BADFD;
      }
          
! 	userp = smb_GetUser(vcp, inp);
  
! 	/*
! 	 * Work around bug in NT client
! 	 *
! 	 * When copying a file, the NT client should first copy the data,
! 	 * then copy the last write time.  But sometimes the NT client does
! 	 * these in the wrong order, so the data copies would inadvertently
! 	 * cause the last write time to be overwritten.  We try to detect this,
! 	 * and don't set client mod time if we think that would go against the
! 	 * intention.
! 	 */
! 	if ((fidp->flags & SMB_FID_LOOKSLIKECOPY) != SMB_FID_LOOKSLIKECOPY) {
! 		fidp->scp->mask |= CM_SCACHEMASK_CLIENTMODTIME;
! 		fidp->scp->clientModTime = time(NULL);
! 	}
  
  #ifndef DJGPP
  	code = smb_WriteData(fidp, &offset, count, op, userp, &written);
  #else /* DJGPP */
  	code = smb_WriteData(fidp, &offset, count, op, userp, &written, FALSE);
  #endif /* !DJGPP */
! 	if (code == 0 && written < count)
! 		code = CM_ERROR_PARTIALWRITE;
  
! 	/* Get a raw buffer */
! 	if (code == 0) {
! 		rawBuf = NULL;
! 		lock_ObtainMutex(&smb_RawBufLock);
! 		if (smb_RawBufs) {
! 			/* Get a raw buf, from head of list */
! 			rawBuf = smb_RawBufs;
  #ifndef DJGPP
! 			smb_RawBufs = *(char **)smb_RawBufs;
  #else /* DJGPP */
              smb_RawBufs = _farpeekl(_dos_ds, smb_RawBufs);
  #endif /* !DJGPP */
! 		}
! 		else
! 			code = CM_ERROR_USESTD;
  		
          lock_ReleaseMutex(&smb_RawBufLock);
! 	}
  
! 	/* Don't allow a premature Close */
! 	if (code == 0 && (writeMode & 1) == 0) {
! 		lock_ObtainMutex(&fidp->mx);
! 		fidp->raw_writers++;
! 		thrd_ResetEvent(fidp->raw_write_event);
! 		lock_ReleaseMutex(&fidp->mx);
! 	}
! 
! 	smb_ReleaseFID(fidp);
! 	cm_ReleaseUser(userp);
! 
! 	if (code) {
! 		smb_SetSMBParm(outp, 0, written);
! 		smb_SetSMBDataLength(outp, 0);
! 		((smb_t *)outp)->com = 0x20;	/* SMB_COM_WRITE_COMPLETE */
! 		rwcp->code = code;
! 		return code;
! 	}
! 
! 	rwcp->code = 0;
! 	rwcp->buf = rawBuf;
! 	rwcp->offset.HighPart = 0;
! 	rwcp->offset.LowPart = offset.LowPart + count;
! 	rwcp->count = totalCount - count;
! 	rwcp->writeMode = writeMode;
! 	rwcp->alreadyWritten = written;
  
! 	/* set the packet data length to 3 bytes for the data block header,
       * plus the size of the data.
       */
! 	smb_SetSMBParm(outp, 0, 0xffff);
      smb_SetSMBDataLength(outp, 0);
  
! 	return 0;
  }
  
  long smb_ReceiveCoreRead(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
! 	osi_hyper_t offset;
      long count, finalCount;
      unsigned short fd;
      smb_fid_t *fidp;
--- 5440,5722 ----
      offset.LowPart = smb_GetSMBParm(inp, 2) | (smb_GetSMBParm(inp, 3) << 16);
  
      op = smb_GetSMBData(inp, NULL);
!     op = smb_ParseDataBlock(op, NULL, &inDataBlockCount);
  
      osi_Log3(smb_logp, "smb_ReceiveCoreWrite fid %d, off 0x%x, size 0x%x",
               fd, offset.LowPart, count);
          
!     fd = smb_ChainFID(fd, inp);
      fidp = smb_FindFID(vcp, fd, 0);
      if (!fidp) {
!         return CM_ERROR_BADFD;
      }
          
      if (fidp->flags & SMB_FID_IOCTL)
          return smb_IoctlWrite(fidp, vcp, inp, outp);
          
!     userp = smb_GetUser(vcp, inp);
  
  	/* special case: 0 bytes transferred means truncate to this position */
      if (count == 0) {
!         cm_req_t req;
  
!         cm_InitReq(&req);
  
!         truncAttr.mask = CM_ATTRMASK_LENGTH;
          truncAttr.length.LowPart = offset.LowPart;
          truncAttr.length.HighPart = 0;
!         lock_ObtainMutex(&fidp->mx);
          code = cm_SetAttr(fidp->scp, &truncAttr, userp, &req);
!         lock_ReleaseMutex(&fidp->mx);
!         smb_SetSMBParm(outp, 0, /* count */ 0);
          smb_SetSMBDataLength(outp, 0);
!         fidp->flags |= SMB_FID_LENGTHSETDONE;
          goto done;
      }
  
!     /*
!      * Work around bug in NT client
!      *
!      * When copying a file, the NT client should first copy the data,
!      * then copy the last write time.  But sometimes the NT client does
!      * these in the wrong order, so the data copies would inadvertently
!      * cause the last write time to be overwritten.  We try to detect this,
!      * and don't set client mod time if we think that would go against the
!      * intention.
!      */
!     if ((fidp->flags & SMB_FID_MTIMESETDONE) != SMB_FID_MTIMESETDONE) {
!         fidp->scp->mask |= CM_SCACHEMASK_CLIENTMODTIME;
!         fidp->scp->clientModTime = time(NULL);
!     }
  
+     code = 0;
+     while ( code == 0 && count > 0 ) {
  #ifndef DJGPP
  	code = smb_WriteData(fidp, &offset, count, op, userp, &written);
  #else /* DJGPP */
  	code = smb_WriteData(fidp, &offset, count, op, userp, &written, FALSE);
  #endif /* !DJGPP */
! 	if (code == 0 && written == 0)
!             code = CM_ERROR_PARTIALWRITE;
  
!         offset.LowPart += written;
!         count -= written;
!         total_written += written;
!         written = 0;
!     }
!     
!     /* set the packet data length to 3 bytes for the data block header,
       * plus the size of the data.
       */
!     smb_SetSMBParm(outp, 0, total_written);
      smb_SetSMBDataLength(outp, 0);
  
    done:
      smb_ReleaseFID(fidp);
      cm_ReleaseUser(userp);
  
!     return code;
  }
  
  void smb_CompleteWriteRaw(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp,
!                           NCB *ncbp, raw_write_cont_t *rwcp)
  {
!     unsigned short fd;
!     smb_fid_t *fidp;
!     cm_user_t *userp;
  #ifndef DJGPP
!     char *rawBuf;
  #else /* DJGPP */
!     dos_ptr rawBuf;
  #endif /* !DJGPP */
!     long written = 0;
!     long code = 0;
  
!     fd = smb_GetSMBParm(inp, 0);
!     fidp = smb_FindFID(vcp, fd, 0);
  
!     osi_Log2(smb_logp, "Completing Raw Write offset %x count %x",
!              rwcp->offset.LowPart, rwcp->count);
  
!     userp = smb_GetUser(vcp, inp);
  
  #ifndef DJGPP
!     rawBuf = rwcp->buf;
!     code = smb_WriteData(fidp, &rwcp->offset, rwcp->count, rawBuf, userp,
  						 &written);
  #else /* DJGPP */
!     rawBuf = (dos_ptr) rwcp->buf;
!     code = smb_WriteData(fidp, &rwcp->offset, rwcp->count,
                           (unsigned char *) rawBuf, userp,
                           &written, TRUE);
  #endif /* !DJGPP */
  
!     if (rwcp->writeMode & 0x1) {	/* synchronous */
!         smb_t *op;
  
!         smb_FormatResponsePacket(vcp, inp, outp);
!         op = (smb_t *) outp;
!         op->com = 0x20;		/* SMB_COM_WRITE_COMPLETE */
!         smb_SetSMBParm(outp, 0, written + rwcp->alreadyWritten);
!         smb_SetSMBDataLength(outp,  0);
!         smb_SendPacket(vcp, outp);
!         smb_FreePacket(outp);
!     }
!     else {				/* asynchronous */
!         lock_ObtainMutex(&fidp->mx);
!         fidp->raw_writers--;
!         if (fidp->raw_writers == 0)
!             thrd_SetEvent(fidp->raw_write_event);
!         lock_ReleaseMutex(&fidp->mx);
!     }
  
!     /* Give back raw buffer */
!     lock_ObtainMutex(&smb_RawBufLock);
  #ifndef DJGPP
!     *((char **)rawBuf) = smb_RawBufs;
  #else /* DJGPP */
      _farpokel(_dos_ds, rawBuf, smb_RawBufs);
  #endif /* !DJGPP */
!     smb_RawBufs = rawBuf;
!     lock_ReleaseMutex(&smb_RawBufLock);
  
!     smb_ReleaseFID(fidp);
!     cm_ReleaseUser(userp);
  }
  
  long smb_ReceiveCoreWriteRawDummy(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
!     return 0;
  }
  
  long smb_ReceiveCoreWriteRaw(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp, raw_write_cont_t *rwcp)
  {
!     osi_hyper_t offset;
!     long count, written = 0, total_written = 0;
!     long totalCount;
      unsigned short fd;
      smb_fid_t *fidp;
      long code = 0;
      cm_user_t *userp;
      char *op;
!     unsigned short writeMode;
  #ifndef DJGPP
!     char *rawBuf;
  #else /* DJGPP */
      dos_ptr rawBuf;
  #endif /* !DJGPP */
  
      fd = smb_GetSMBParm(inp, 0);
!     totalCount = smb_GetSMBParm(inp, 1);
      count = smb_GetSMBParm(inp, 10);
      offset.HighPart = 0;	/* too bad */
      offset.LowPart = smb_GetSMBParm(inp, 3) | (smb_GetSMBParm(inp, 4) << 16);
!     writeMode = smb_GetSMBParm(inp, 7);
  
!     op = (char *) inp->data;
!     op += smb_GetSMBParm(inp, 11);
  
      osi_Log4(smb_logp,
               "smb_ReceiveCoreWriteRaw fd %d, off 0x%x, size 0x%x, WriteMode 0x%x",
               fd, offset.LowPart, count, writeMode);
          
!     fd = smb_ChainFID(fd, inp);
      fidp = smb_FindFID(vcp, fd, 0);
      if (!fidp) {
!         return CM_ERROR_BADFD;
      }
          
!     userp = smb_GetUser(vcp, inp);
  
!     /*
!      * Work around bug in NT client
!      *
!      * When copying a file, the NT client should first copy the data,
!      * then copy the last write time.  But sometimes the NT client does
!      * these in the wrong order, so the data copies would inadvertently
!      * cause the last write time to be overwritten.  We try to detect this,
!      * and don't set client mod time if we think that would go against the
!      * intention.
!      */
!     if ((fidp->flags & SMB_FID_LOOKSLIKECOPY) != SMB_FID_LOOKSLIKECOPY) {
!         fidp->scp->mask |= CM_SCACHEMASK_CLIENTMODTIME;
!         fidp->scp->clientModTime = time(NULL);
!     }
  
+     code = 0;
+     while ( code == 0 && count > 0 ) {
  #ifndef DJGPP
  	code = smb_WriteData(fidp, &offset, count, op, userp, &written);
  #else /* DJGPP */
  	code = smb_WriteData(fidp, &offset, count, op, userp, &written, FALSE);
  #endif /* !DJGPP */
! 	if (code == 0 && written == 0)
!             code = CM_ERROR_PARTIALWRITE;
  
!         offset.LowPart += written;
!         count -= written;
!         total_written += written;
!         written = 0;
!     }
! 
!     /* Get a raw buffer */
!     if (code == 0) {
!         rawBuf = NULL;
!         lock_ObtainMutex(&smb_RawBufLock);
!         if (smb_RawBufs) {
!             /* Get a raw buf, from head of list */
!             rawBuf = smb_RawBufs;
  #ifndef DJGPP
!             smb_RawBufs = *(char **)smb_RawBufs;
  #else /* DJGPP */
              smb_RawBufs = _farpeekl(_dos_ds, smb_RawBufs);
  #endif /* !DJGPP */
!         }
!         else
!             code = CM_ERROR_USESTD;
  		
          lock_ReleaseMutex(&smb_RawBufLock);
!     }
! 
!     /* Don't allow a premature Close */
!     if (code == 0 && (writeMode & 1) == 0) {
!         lock_ObtainMutex(&fidp->mx);
!         fidp->raw_writers++;
!         thrd_ResetEvent(fidp->raw_write_event);
!         lock_ReleaseMutex(&fidp->mx);
!     }
! 
!     smb_ReleaseFID(fidp);
!     cm_ReleaseUser(userp);
  
!     if (code) {
!         smb_SetSMBParm(outp, 0, total_written);
!         smb_SetSMBDataLength(outp, 0);
!         ((smb_t *)outp)->com = 0x20;	/* SMB_COM_WRITE_COMPLETE */
!         rwcp->code = code;
!         return code;
!     }
  
!     rwcp->code = 0;
!     rwcp->buf = rawBuf;
!     rwcp->offset.HighPart = 0;
!     rwcp->offset.LowPart = offset.LowPart + count;
!     rwcp->count = totalCount - count;
!     rwcp->writeMode = writeMode;
!     rwcp->alreadyWritten = total_written;
! 
!     /* set the packet data length to 3 bytes for the data block header,
       * plus the size of the data.
       */
!     smb_SetSMBParm(outp, 0, 0xffff);
      smb_SetSMBDataLength(outp, 0);
  
!     return 0;
  }
  
  long smb_ReceiveCoreRead(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
!     osi_hyper_t offset;
      long count, finalCount;
      unsigned short fd;
      smb_fid_t *fidp;
***************
*** 5546,5594 ****
      osi_Log3(smb_logp, "smb_ReceiveCoreRead fd %d, off 0x%x, size 0x%x",
               fd, offset.LowPart, count);
          
! 	fd = smb_ChainFID(fd, inp);
      fidp = smb_FindFID(vcp, fd, 0);
      if (!fidp) {
! 		return CM_ERROR_BADFD;
      }
          
      if (fidp->flags & SMB_FID_IOCTL) {
! 		return smb_IoctlRead(fidp, vcp, inp, outp);
      }
          
! 	userp = smb_GetUser(vcp, inp);
  
! 	/* remember this for final results */
      smb_SetSMBParm(outp, 0, count);
      smb_SetSMBParm(outp, 1, 0);
      smb_SetSMBParm(outp, 2, 0);
      smb_SetSMBParm(outp, 3, 0);
      smb_SetSMBParm(outp, 4, 0);
  
! 	/* set the packet data length to 3 bytes for the data block header,
       * plus the size of the data.
       */
      smb_SetSMBDataLength(outp, count+3);
          
! 	/* get op ptr after putting in the parms, since otherwise we don't
       * know where the data really is.
       */
      op = smb_GetSMBData(outp, NULL);
  
! 	/* now emit the data block header: 1 byte of type and 2 bytes of length */
      *op++ = 1;	/* data block marker */
      *op++ = (unsigned char) (count & 0xff);
      *op++ = (unsigned char) ((count >> 8) & 0xff);
                  
  #ifndef DJGPP
! 	code = smb_ReadData(fidp, &offset, count, op, userp, &finalCount);
  #else /* DJGPP */
      code = smb_ReadData(fidp, &offset, count, op, userp, &finalCount, FALSE);
  #endif /* !DJGPP */
  
! 	/* fix some things up */
! 	smb_SetSMBParm(outp, 0, finalCount);
! 	smb_SetSMBDataLength(outp, finalCount+3);
  
      smb_ReleaseFID(fidp);
  	
--- 5732,5780 ----
      osi_Log3(smb_logp, "smb_ReceiveCoreRead fd %d, off 0x%x, size 0x%x",
               fd, offset.LowPart, count);
          
!     fd = smb_ChainFID(fd, inp);
      fidp = smb_FindFID(vcp, fd, 0);
      if (!fidp) {
!         return CM_ERROR_BADFD;
      }
          
      if (fidp->flags & SMB_FID_IOCTL) {
!         return smb_IoctlRead(fidp, vcp, inp, outp);
      }
          
!     userp = smb_GetUser(vcp, inp);
  
!     /* remember this for final results */
      smb_SetSMBParm(outp, 0, count);
      smb_SetSMBParm(outp, 1, 0);
      smb_SetSMBParm(outp, 2, 0);
      smb_SetSMBParm(outp, 3, 0);
      smb_SetSMBParm(outp, 4, 0);
  
!     /* set the packet data length to 3 bytes for the data block header,
       * plus the size of the data.
       */
      smb_SetSMBDataLength(outp, count+3);
          
!     /* get op ptr after putting in the parms, since otherwise we don't
       * know where the data really is.
       */
      op = smb_GetSMBData(outp, NULL);
  
!     /* now emit the data block header: 1 byte of type and 2 bytes of length */
      *op++ = 1;	/* data block marker */
      *op++ = (unsigned char) (count & 0xff);
      *op++ = (unsigned char) ((count >> 8) & 0xff);
                  
  #ifndef DJGPP
!     code = smb_ReadData(fidp, &offset, count, op, userp, &finalCount);
  #else /* DJGPP */
      code = smb_ReadData(fidp, &offset, count, op, userp, &finalCount, FALSE);
  #endif /* !DJGPP */
  
!     /* fix some things up */
!     smb_SetSMBParm(outp, 0, finalCount);
!     smb_SetSMBDataLength(outp, finalCount+3);
  
      smb_ReleaseFID(fidp);
  	
***************
*** 5598,5606 ****
  
  long smb_ReceiveCoreMakeDir(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
! 	char *pathp;
      long code = 0;
! 	cm_space_t *spacep;
      char *tp;
      cm_user_t *userp;
      cm_scache_t *dscp;			/* dir we're dealing with */
--- 5784,5792 ----
  
  long smb_ReceiveCoreMakeDir(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
!     char *pathp;
      long code = 0;
!     cm_space_t *spacep;
      char *tp;
      cm_user_t *userp;
      cm_scache_t *dscp;			/* dir we're dealing with */
***************
*** 5609,5644 ****
      int initialModeBits;
      char *lastNamep;
      int caseFold;
! 	char *tidPathp;
! 	cm_req_t req;
  
! 	cm_InitReq(&req);
  
      scp = NULL;
          
! 	/* compute initial mode bits based on read-only flag in attributes */
      initialModeBits = 0777;
          
! 	tp = smb_GetSMBData(inp, NULL);
      pathp = smb_ParseASCIIBlock(tp, &tp);
  
! 	if (strcmp(pathp, "\\") == 0)
! 		return CM_ERROR_EXISTS;
  
! 	spacep = inp->spacep;
      smb_StripLastComponent(spacep->data, &lastNamep, pathp);
  
! 	userp = smb_GetUser(vcp, inp);
  
      caseFold = CM_FLAG_CASEFOLD;
  
! 	code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!     if(code) {
          cm_ReleaseUser(userp);
          return CM_ERROR_NOSUCHPATH;
      }
  
! 	code = cm_NameI(cm_rootSCachep, spacep->data,
                      caseFold | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH,
                      userp, tidPathp, &req, &dscp);
  
--- 5795,5830 ----
      int initialModeBits;
      char *lastNamep;
      int caseFold;
!     char *tidPathp;
!     cm_req_t req;
  
!     cm_InitReq(&req);
  
      scp = NULL;
          
!     /* compute initial mode bits based on read-only flag in attributes */
      initialModeBits = 0777;
          
!     tp = smb_GetSMBData(inp, NULL);
      pathp = smb_ParseASCIIBlock(tp, &tp);
  
!     if (strcmp(pathp, "\\") == 0)
!         return CM_ERROR_EXISTS;
  
!     spacep = inp->spacep;
      smb_StripLastComponent(spacep->data, &lastNamep, pathp);
  
!     userp = smb_GetUser(vcp, inp);
  
      caseFold = CM_FLAG_CASEFOLD;
  
!     code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!     if (code) {
          cm_ReleaseUser(userp);
          return CM_ERROR_NOSUCHPATH;
      }
  
!     code = cm_NameI(cm_rootSCachep, spacep->data,
                      caseFold | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH,
                      userp, tidPathp, &req, &dscp);
  
***************
*** 5648,5654 ****
      }
          
      /* otherwise, scp points to the parent directory.  Do a lookup, and
! 	 * fail if we find it.  Otherwise, we do the create.
       */
      if (!lastNamep) 
          lastNamep = pathp;
--- 5834,5840 ----
      }
          
      /* otherwise, scp points to the parent directory.  Do a lookup, and
!      * fail if we find it.  Otherwise, we do the create.
       */
      if (!lastNamep) 
          lastNamep = pathp;
***************
*** 5658,5686 ****
      if (scp) cm_ReleaseSCache(scp);
      if (code != CM_ERROR_NOSUCHFILE) {
          if (code == 0) code = CM_ERROR_EXISTS;
! 		cm_ReleaseSCache(dscp);
          cm_ReleaseUser(userp);
          return code;
      }
          
! 	setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
! 	setAttr.clientModTime = time(NULL);
! 	code = cm_MakeDir(dscp, lastNamep, 0, &setAttr, userp, &req);
! 	if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
! 		smb_NotifyChange(FILE_ACTION_ADDED,
                           FILE_NOTIFY_CHANGE_DIR_NAME,
                           dscp, lastNamep, NULL, TRUE);
          
! 	/* we don't need this any longer */
! 	cm_ReleaseSCache(dscp);
  
      if (code) {
! 		/* something went wrong creating or truncating the file */
          cm_ReleaseUser(userp);
          return code;
      }
          
! 	/* otherwise we succeeded */
      smb_SetSMBDataLength(outp, 0);
      cm_ReleaseUser(userp);
  
--- 5844,5872 ----
      if (scp) cm_ReleaseSCache(scp);
      if (code != CM_ERROR_NOSUCHFILE) {
          if (code == 0) code = CM_ERROR_EXISTS;
!         cm_ReleaseSCache(dscp);
          cm_ReleaseUser(userp);
          return code;
      }
          
!     setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
!     setAttr.clientModTime = time(NULL);
!     code = cm_MakeDir(dscp, lastNamep, 0, &setAttr, userp, &req);
!     if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
!         smb_NotifyChange(FILE_ACTION_ADDED,
                           FILE_NOTIFY_CHANGE_DIR_NAME,
                           dscp, lastNamep, NULL, TRUE);
          
!     /* we don't need this any longer */
!     cm_ReleaseSCache(dscp);
  
      if (code) {
!         /* something went wrong creating or truncating the file */
          cm_ReleaseUser(userp);
          return code;
      }
          
!     /* otherwise we succeeded */
      smb_SetSMBDataLength(outp, 0);
      cm_ReleaseUser(userp);
  
***************
*** 5703,5711 ****
  
  long smb_ReceiveCoreCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
! 	char *pathp;
      long code = 0;
! 	cm_space_t *spacep;
      char *tp;
      int excl;
      cm_user_t *userp;
--- 5889,5897 ----
  
  long smb_ReceiveCoreCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  {
!     char *pathp;
      long code = 0;
!     cm_space_t *spacep;
      char *tp;
      int excl;
      cm_user_t *userp;
***************
*** 5718,5727 ****
      char *lastNamep;
      int caseFold;
      long dosTime;
! 	char *tidPathp;
! 	cm_req_t req;
  
! 	cm_InitReq(&req);
  
      scp = NULL;
      excl = (inp->inCom == 0x03)? 0 : 1;
--- 5904,5913 ----
      char *lastNamep;
      int caseFold;
      long dosTime;
!     char *tidPathp;
!     cm_req_t req;
  
!     cm_InitReq(&req);
  
      scp = NULL;
      excl = (inp->inCom == 0x03)? 0 : 1;
***************
*** 5729,5754 ****
      attributes = smb_GetSMBParm(inp, 0);
      dosTime = smb_GetSMBParm(inp, 1) | (smb_GetSMBParm(inp, 2) << 16);
          
! 	/* compute initial mode bits based on read-only flag in attributes */
      initialModeBits = 0666;
      if (attributes & 1) initialModeBits &= ~0222;
          
! 	tp = smb_GetSMBData(inp, NULL);
      pathp = smb_ParseASCIIBlock(tp, &tp);
  
! 	spacep = inp->spacep;
      smb_StripLastComponent(spacep->data, &lastNamep, pathp);
  
! 	userp = smb_GetUser(vcp, inp);
  
      caseFold = CM_FLAG_CASEFOLD;
  
! 	code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!     if(code) {
          cm_ReleaseUser(userp);
          return CM_ERROR_NOSUCHPATH;
      }
! 	code = cm_NameI(cm_rootSCachep, spacep->data, caseFold | CM_FLAG_FOLLOW,
                      userp, tidPathp, &req, &dscp);
  
      if (code) {
--- 5915,5940 ----
      attributes = smb_GetSMBParm(inp, 0);
      dosTime = smb_GetSMBParm(inp, 1) | (smb_GetSMBParm(inp, 2) << 16);
          
!     /* compute initial mode bits based on read-only flag in attributes */
      initialModeBits = 0666;
      if (attributes & 1) initialModeBits &= ~0222;
          
!     tp = smb_GetSMBData(inp, NULL);
      pathp = smb_ParseASCIIBlock(tp, &tp);
  
!     spacep = inp->spacep;
      smb_StripLastComponent(spacep->data, &lastNamep, pathp);
  
!     userp = smb_GetUser(vcp, inp);
  
      caseFold = CM_FLAG_CASEFOLD;
  
!     code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
!     if (code) {
          cm_ReleaseUser(userp);
          return CM_ERROR_NOSUCHPATH;
      }
!     code = cm_NameI(cm_rootSCachep, spacep->data, caseFold | CM_FLAG_FOLLOW,
                      userp, tidPathp, &req, &dscp);
  
      if (code) {
***************
*** 5757,5763 ****
      }
          
      /* otherwise, scp points to the parent directory.  Do a lookup, and
! 	 * truncate the file if we find it, otherwise we create the file.
       */
      if (!lastNamep) lastNamep = pathp;
      else lastNamep++;
--- 5943,5949 ----
      }
          
      /* otherwise, scp points to the parent directory.  Do a lookup, and
!      * truncate the file if we find it, otherwise we create the file.
       */
      if (!lastNamep) lastNamep = pathp;
      else lastNamep++;
***************
*** 5777,5783 ****
  
      code = cm_Lookup(dscp, lastNamep, 0, userp, &req, &scp);
      if (code && code != CM_ERROR_NOSUCHFILE) {
! 		cm_ReleaseSCache(dscp);
          cm_ReleaseUser(userp);
          return code;
      }
--- 5963,5969 ----
  
      code = cm_Lookup(dscp, lastNamep, 0, userp, &req, &scp);
      if (code && code != CM_ERROR_NOSUCHFILE) {
!         cm_ReleaseSCache(dscp);
          cm_ReleaseUser(userp);
          return code;
      }
***************
*** 5785,5824 ****
      /* if we get here, if code is 0, the file exists and is represented by
       * scp.  Otherwise, we have to create it.
       */
! 	if (code == 0) {
! 		if (excl) {
! 			/* oops, file shouldn't be there */
              cm_ReleaseSCache(dscp);
              cm_ReleaseSCache(scp);
              cm_ReleaseUser(userp);
              return CM_ERROR_EXISTS;
          }
  
! 		setAttr.mask = CM_ATTRMASK_LENGTH;
          setAttr.length.LowPart = 0;
          setAttr.length.HighPart = 0;
! 		code = cm_SetAttr(scp, &setAttr, userp, &req);
      }
      else {
! 		setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
! 		smb_UnixTimeFromDosUTime(&setAttr.clientModTime, dosTime);
          code = cm_Create(dscp, lastNamep, 0, &setAttr, &scp, userp,
                           &req);
! 		if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
! 			smb_NotifyChange(FILE_ACTION_ADDED,
                               FILE_NOTIFY_CHANGE_FILE_NAME,
                               dscp, lastNamep, NULL, TRUE);
          if (!excl && code == CM_ERROR_EXISTS) {
! 			/* not an exclusive create, and someone else tried
! 			 * creating it already, then we open it anyway.  We
! 			 * don't bother retrying after this, since if this next
! 			 * fails, that means that the file was deleted after
! 			 * we started this call.
               */
              code = cm_Lookup(dscp, lastNamep, caseFold, userp,
                               &req, &scp);
              if (code == 0) {
! 				setAttr.mask = CM_ATTRMASK_LENGTH;
                  setAttr.length.LowPart = 0;
                  setAttr.length.HighPart = 0;
                  code = cm_SetAttr(scp, &setAttr, userp, &req);
--- 5971,6010 ----
      /* if we get here, if code is 0, the file exists and is represented by
       * scp.  Otherwise, we have to create it.
       */
!     if (code == 0) {
!         if (excl) {
!             /* oops, file shouldn't be there */
              cm_ReleaseSCache(dscp);
              cm_ReleaseSCache(scp);
              cm_ReleaseUser(userp);
              return CM_ERROR_EXISTS;
          }
  
!         setAttr.mask = CM_ATTRMASK_LENGTH;
          setAttr.length.LowPart = 0;
          setAttr.length.HighPart = 0;
!         code = cm_SetAttr(scp, &setAttr, userp, &req);
      }
      else {
!         setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
!         smb_UnixTimeFromDosUTime(&setAttr.clientModTime, dosTime);
          code = cm_Create(dscp, lastNamep, 0, &setAttr, &scp, userp,
                           &req);
!         if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
!             smb_NotifyChange(FILE_ACTION_ADDED,
                               FILE_NOTIFY_CHANGE_FILE_NAME,
                               dscp, lastNamep, NULL, TRUE);
          if (!excl && code == CM_ERROR_EXISTS) {
!             /* not an exclusive create, and someone else tried
!              * creating it already, then we open it anyway.  We
!              * don't bother retrying after this, since if this next
!              * fails, that means that the file was deleted after
!              * we started this call.
               */
              code = cm_Lookup(dscp, lastNamep, caseFold, userp,
                               &req, &scp);
              if (code == 0) {
!                 setAttr.mask = CM_ATTRMASK_LENGTH;
                  setAttr.length.LowPart = 0;
                  setAttr.length.HighPart = 0;
                  code = cm_SetAttr(scp, &setAttr, userp, &req);
***************
*** 5826,5864 ****
          }
      }
          
! 	/* we don't need this any longer */
! 	cm_ReleaseSCache(dscp);
  
      if (code) {
! 		/* something went wrong creating or truncating the file */
          if (scp) cm_ReleaseSCache(scp);
          cm_ReleaseUser(userp);
          return code;
      }
  
! 	/* make sure we only open files */
! 	if (scp->fileType != CM_SCACHETYPE_FILE) {
! 		cm_ReleaseSCache(scp);
          cm_ReleaseUser(userp);
          return CM_ERROR_ISDIR;
! 	}
  
      /* now all we have to do is open the file itself */
      fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
      osi_assert(fidp);
  	
! 	/* save a pointer to the vnode */
      fidp->scp = scp;
          
! 	/* always create it open for read/write */
! 	fidp->flags |= (SMB_FID_OPENREAD | SMB_FID_OPENWRITE);
  
! 	smb_ReleaseFID(fidp);
          
! 	smb_SetSMBParm(outp, 0, fidp->fid);
      smb_SetSMBDataLength(outp, 0);
  
! 	cm_Open(scp, 0, userp);
  
      cm_ReleaseUser(userp);
      /* leave scp held since we put it in fidp->scp */
--- 6012,6050 ----
          }
      }
          
!     /* we don't need this any longer */
!     cm_ReleaseSCache(dscp);
  
      if (code) {
!         /* something went wrong creating or truncating the file */
          if (scp) cm_ReleaseSCache(scp);
          cm_ReleaseUser(userp);
          return code;
      }
  
!     /* make sure we only open files */
!     if (scp->fileType != CM_SCACHETYPE_FILE) {
!         cm_ReleaseSCache(scp);
          cm_ReleaseUser(userp);
          return CM_ERROR_ISDIR;
!     }
  
      /* now all we have to do is open the file itself */
      fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
      osi_assert(fidp);
  	
!     /* save a pointer to the vnode */
      fidp->scp = scp;
          
!     /* always create it open for read/write */
!     fidp->flags |= (SMB_FID_OPENREAD | SMB_FID_OPENWRITE);
  
!     smb_ReleaseFID(fidp);
          
!     smb_SetSMBParm(outp, 0, fidp->fid);
      smb_SetSMBDataLength(outp, 0);
  
!     cm_Open(scp, 0, userp);
  
      cm_ReleaseUser(userp);
      /* leave scp held since we put it in fidp->scp */
***************
*** 5874,5916 ****
      smb_fid_t *fidp;
      cm_scache_t *scp;
      cm_user_t *userp;
! 	cm_req_t req;
  
! 	cm_InitReq(&req);
          
      fd = smb_GetSMBParm(inp, 0);
! 	whence = smb_GetSMBParm(inp, 1);
      offset = smb_GetSMBParm(inp, 2) | (smb_GetSMBParm(inp, 3) << 16);
          
! 	/* try to find the file descriptor */
! 	fd = smb_ChainFID(fd, inp);
      fidp = smb_FindFID(vcp, fd, 0);
      if (!fidp || (fidp->flags & SMB_FID_IOCTL)) {
! 		return CM_ERROR_BADFD;
      }
  	
! 	userp = smb_GetUser(vcp, inp);
  
      lock_ObtainMutex(&fidp->mx);
      scp = fidp->scp;
! 	lock_ObtainMutex(&scp->mx);
! 	code = cm_SyncOp(scp, NULL, userp, &req, 0,
                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
! 	if (code == 0) {
! 		if (whence == 1) {
              /* offset from current offset */
              offset += fidp->offset;
! 		}
! 		else if (whence == 2) {
              /* offset from current EOF */
              offset += scp->length.LowPart;
! 		}
          fidp->offset = offset;
          smb_SetSMBParm(outp, 0, offset & 0xffff);
          smb_SetSMBParm(outp, 1, (offset>>16) & 0xffff);
          smb_SetSMBDataLength(outp, 0);
      }
! 	lock_ReleaseMutex(&scp->mx);
      lock_ReleaseMutex(&fidp->mx);
      smb_ReleaseFID(fidp);
      cm_ReleaseUser(userp);
--- 6060,6102 ----
      smb_fid_t *fidp;
      cm_scache_t *scp;
      cm_user_t *userp;
!     cm_req_t req;
  
!     cm_InitReq(&req);
          
      fd = smb_GetSMBParm(inp, 0);
!     whence = smb_GetSMBParm(inp, 1);
      offset = smb_GetSMBParm(inp, 2) | (smb_GetSMBParm(inp, 3) << 16);
          
!     /* try to find the file descriptor */
!     fd = smb_ChainFID(fd, inp);
      fidp = smb_FindFID(vcp, fd, 0);
      if (!fidp || (fidp->flags & SMB_FID_IOCTL)) {
!         return CM_ERROR_BADFD;
      }
  	
!     userp = smb_GetUser(vcp, inp);
  
      lock_ObtainMutex(&fidp->mx);
      scp = fidp->scp;
!     lock_ObtainMutex(&scp->mx);
!     code = cm_SyncOp(scp, NULL, userp, &req, 0,
                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
!     if (code == 0) {
!         if (whence == 1) {
              /* offset from current offset */
              offset += fidp->offset;
!         }
!         else if (whence == 2) {
              /* offset from current EOF */
              offset += scp->length.LowPart;
!         }
          fidp->offset = offset;
          smb_SetSMBParm(outp, 0, offset & 0xffff);
          smb_SetSMBParm(outp, 1, (offset>>16) & 0xffff);
          smb_SetSMBDataLength(outp, 0);
      }
!     lock_ReleaseMutex(&scp->mx);
      lock_ReleaseMutex(&fidp->mx);
      smb_ReleaseFID(fidp);
      cm_ReleaseUser(userp);
***************
*** 5921,5927 ****
   * be more than one request.
   */
  void smb_DispatchPacket(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp,
! 	NCB *ncbp, raw_write_cont_t *rwcp)
  {
      smb_dispatch_t *dp;
      smb_t *smbp;
--- 6107,6113 ----
   * be more than one request.
   */
  void smb_DispatchPacket(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp,
!                         NCB *ncbp, raw_write_cont_t *rwcp)
  {
      smb_dispatch_t *dp;
      smb_t *smbp;
***************
*** 5933,6157 ****
      int temp;
      unsigned char *tp;
      unsigned short errCode;
! 	unsigned long NTStatus;
      int noSend;
      unsigned char errClass;
! 	unsigned int oldGen;
! 	DWORD oldTime, newTime;
  
! 	/* get easy pointer to the data */
! 	smbp = (smb_t *) inp->data;
  
! 	if (!(outp->flags & SMB_PACKETFLAG_SUSPENDED)) {
          /* setup the basic parms for the initial request in the packet */
! 		inp->inCom = smbp->com;
          inp->wctp = &smbp->wct;
          inp->inCount = 0;
! 		inp->ncb_length = ncbp->ncb_length;
! 	}
      noSend = 0;
  
! 	/* Sanity check */
! 	if (ncbp->ncb_length < offsetof(struct smb, vdata)) {
! 		/* log it and discard it */
! #ifndef DJGPP
! 		HANDLE h;
! 		char *ptbuf[1];
! 		char s[100];
! 		h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
! 		sprintf(s, "SMB message too short, len %d", ncbp->ncb_length);
! 		ptbuf[0] = s;
! 		ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1007, NULL,
! 			1, ncbp->ncb_length, ptbuf, inp);
! 		DeregisterEventSource(h);
  #else /* DJGPP */
          osi_Log1(smb_logp, "SMB message too short, len %d", ncbp->ncb_length);
  #endif /* !DJGPP */
! 		return;
! 	}
  
! 	/* We are an ongoing op */
! 	thrd_Increment(&ongoingOps);
  
      /* set up response packet for receiving output */
! 	if (!(outp->flags & SMB_PACKETFLAG_SUSPENDED))
          smb_FormatResponsePacket(vcp, inp, outp);
      outWctp = outp->wctp;
  
! 	/* Remember session generation number and time */
! 	oldGen = sessionGen;
! 	oldTime = GetCurrentTime();
  
! 	while(inp->inCom != 0xff) {
          dp = &smb_dispatchTable[inp->inCom];
  
! 		if (outp->flags & SMB_PACKETFLAG_SUSPENDED) {
! 			outp->flags &= ~SMB_PACKETFLAG_SUSPENDED;
! 			code = outp->resumeCode;
! 			goto resume;
! 		}
  
          /* process each request in the packet; inCom, wctp and inCount
           * are already set up.
           */
! 		osi_Log2(smb_logp, "SMB received op 0x%x lsn %d", inp->inCom,
!                  ncbp->ncb_lsn);
  
! 		/* now do the dispatch */
! 		/* start by formatting the response record a little, as a default */
          if (dp->flags & SMB_DISPATCHFLAG_CHAINED) {
! 			outWctp[0] = 2;
              outWctp[1] = 0xff;	/* no operation */
              outWctp[2] = 0;		/* padding */
              outWctp[3] = 0;
              outWctp[4] = 0;
          }
! 		else {
! 			/* not a chained request, this is a more reasonable default */
              outWctp[0] = 0;	/* wct of zero */
              outWctp[1] = 0;	/* and bcc (word) of zero */
              outWctp[2] = 0;
! 		}   
  
! 		/* once set, stays set.  Doesn't matter, since we never chain
           * "no response" calls.
           */
! 		if (dp->flags & SMB_DISPATCHFLAG_NORESPONSE)
              noSend = 1;
  
          if (dp->procp) {
! 			/* we have a recognized operation */
  
! 			if (inp->inCom == 0x1d)
! 				/* Raw Write */
! 				code = smb_ReceiveCoreWriteRaw (vcp, inp, outp,
!                                                 rwcp);
! 			else {
! 					osi_LogEvent("AFS Dispatch %s",(myCrt_Dispatch(inp->inCom)),"vcp[%x] lana[%d] lsn[%d]",(int)vcp,vcp->lana,vcp->lsn);
! 					osi_Log4(smb_logp,"Dispatch %s vcp[%x] lana[%d] lsn[%d]",(myCrt_Dispatch(inp->inCom)),vcp,vcp->lana,vcp->lsn);
! 					code = (*(dp->procp)) (vcp, inp, outp);
! 					osi_LogEvent("AFS Dispatch return",NULL,"Code[%d]",(code==0)?0:code-CM_ERROR_BASE);
! 					osi_Log1(smb_logp,"Dispatch return  code[%d]",(code==0)?0:code-CM_ERROR_BASE);
!             }
! 
! 			if (oldGen != sessionGen) {
! #ifndef DJGPP
! 				HANDLE h;
! 				char *ptbuf[1];
! 				char s[100];
! 				newTime = GetCurrentTime();
! 				h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
! 				sprintf(s, "Pkt straddled session startup, took %d ms, ncb length %d",
!                         newTime - oldTime, ncbp->ncb_length);
! 				ptbuf[0] = s;
! 				ReportEvent(h, EVENTLOG_WARNING_TYPE, 0,
!                             1005, NULL, 1, ncbp->ncb_length, ptbuf, smbp);
! 				DeregisterEventSource(h);
  #endif /* !DJGPP */
! 				osi_Log1(smb_logp, "Pkt straddled session startup, "
!                          "ncb length %d", ncbp->ncb_length);
! 			}
          }
          else {
! 			/* bad opcode, fail the request, after displaying it */
! #ifdef NOTSERVICE
              smb_LogPacket(inp);
! #endif  /* NOTSERVICE */
  
  #ifndef DJGPP
! 			if (showErrors) {
! 				sprintf(tbuffer, "Received bad SMB req 0x%x", inp->inCom);
                  code = (*smb_MBfunc)(NULL, tbuffer, "Cancel: don't show again",
!                                      MB_OKCANCEL|MB_SERVICE_NOTIFICATION);
!                 if (code == IDCANCEL) showErrors = 0;
! 			}
  #endif /* DJGPP */
              code = CM_ERROR_BADOP;
          }
  
! 		/* catastrophic failure:  log as much as possible */
! 		if (code == CM_ERROR_BADSMB) {
  #ifndef DJGPP
! 			HANDLE h;
! 			char *ptbuf[1];
! 			char s[100];
  
! 			osi_Log1(smb_logp,
                        "Invalid SMB, ncb_length %d",
                        ncbp->ncb_length);
  
! 			h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
! 			sprintf(s, "Invalid SMB message, length %d",
                       ncbp->ncb_length);
! 			ptbuf[0] = s;
! 			ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, 1002, NULL,
                           1, ncbp->ncb_length, ptbuf, smbp);
! 			DeregisterEventSource(h);
! #ifdef NOTSERVICE
              smb_LogPacket(inp);
! #endif /* NOTSERVICE */
  #endif /* !DJGPP */
              osi_Log1(smb_logp, "Invalid SMB message, length %d",
                       ncbp->ncb_length);
  
! 			code = CM_ERROR_INVAL;
! 		}
  
! 		if (outp->flags & SMB_PACKETFLAG_NOSEND) {
! 			thrd_Decrement(&ongoingOps);
! 			return;
! 		}
  
        resume:
! 		/* now, if we failed, turn the current response into an empty
           * one, and fill in the response packet's error code.
           */
! 		if (code) {
! 			if (vcp->flags & SMB_VCFLAG_STATUS32) {
! 				smb_MapNTError(code, &NTStatus);
! 				outWctp = outp->wctp;
! 				smbp = (smb_t *) &outp->data;
! 				if (code != CM_ERROR_PARTIALWRITE
! 				    && code != CM_ERROR_BUFFERTOOSMALL 
!                     && code != CM_ERROR_GSSCONTINUE) {
! 					/* nuke wct and bcc.  For a partial
! 					 * write or an in-process authentication handshake, 
                       * assume they're OK.
! 					 */
! 					*outWctp++ = 0;
! 					*outWctp++ = 0;
! 					*outWctp++ = 0;
! 				}
! 				smbp->rcls = (unsigned char) (NTStatus & 0xff);
! 				smbp->reh = (unsigned char) ((NTStatus >> 8) & 0xff);
! 				smbp->errLow = (unsigned char) ((NTStatus >> 16) & 0xff);
! 				smbp->errHigh = (unsigned char) ((NTStatus >> 24) & 0xff);
! 				smbp->flg2 |= 0x4000;
! 				break;
! 			}
! 			else {
                  smb_MapCoreError(code, vcp, &errCode, &errClass);
! 				outWctp = outp->wctp;
! 				smbp = (smb_t *) &outp->data;
! 				if (code != CM_ERROR_PARTIALWRITE) {
! 					/* nuke wct and bcc.  For a partial
! 					 * write, assume they're OK.
! 					 */
! 					*outWctp++ = 0;
! 					*outWctp++ = 0;
! 					*outWctp++ = 0;
! 				}
! 				smbp->errLow = (unsigned char) (errCode & 0xff);
! 				smbp->errHigh = (unsigned char) ((errCode >> 8) & 0xff);
                  smbp->rcls = errClass;
! 				break;
! 			}
! 		}	/* error occurred */
!                 
          /* if we're here, we've finished one request.  Look to see if
! 		 * this is a chained opcode.  If it is, setup things to process
! 		 * the chained request, and setup the output buffer to hold the
! 		 * chained response.  Start by finding the next input record.
           */
          if (!(dp->flags & SMB_DISPATCHFLAG_CHAINED))
              break;		/* not a chained req */
--- 6119,6350 ----
      int temp;
      unsigned char *tp;
      unsigned short errCode;
!     unsigned long NTStatus;
      int noSend;
      unsigned char errClass;
!     unsigned int oldGen;
!     DWORD oldTime, newTime;
  
!     /* get easy pointer to the data */
!     smbp = (smb_t *) inp->data;
  
!     if (!(outp->flags & SMB_PACKETFLAG_SUSPENDED)) {
          /* setup the basic parms for the initial request in the packet */
!         inp->inCom = smbp->com;
          inp->wctp = &smbp->wct;
          inp->inCount = 0;
!         inp->ncb_length = ncbp->ncb_length;
!     }
      noSend = 0;
  
!     /* Sanity check */
!     if (ncbp->ncb_length < offsetof(struct smb, vdata)) {
!         /* log it and discard it */
! #ifndef DJGPP
!         HANDLE h;
!         char *ptbuf[1];
!         char s[100];
!         h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
!         sprintf(s, "SMB message too short, len %d", ncbp->ncb_length);
!         ptbuf[0] = s;
!         ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1007, NULL,
!                      1, ncbp->ncb_length, ptbuf, inp);
!         DeregisterEventSource(h);
  #else /* DJGPP */
          osi_Log1(smb_logp, "SMB message too short, len %d", ncbp->ncb_length);
  #endif /* !DJGPP */
!         return;
!     }
  
!     /* We are an ongoing op */
!     thrd_Increment(&ongoingOps);
  
      /* set up response packet for receiving output */
!     if (!(outp->flags & SMB_PACKETFLAG_SUSPENDED))
          smb_FormatResponsePacket(vcp, inp, outp);
      outWctp = outp->wctp;
  
!     /* Remember session generation number and time */
!     oldGen = sessionGen;
!     oldTime = GetCurrentTime();
  
!     while (inp->inCom != 0xff) {
          dp = &smb_dispatchTable[inp->inCom];
  
!         if (outp->flags & SMB_PACKETFLAG_SUSPENDED) {
!             outp->flags &= ~SMB_PACKETFLAG_SUSPENDED;
!             code = outp->resumeCode;
!             goto resume;
!         }
  
          /* process each request in the packet; inCom, wctp and inCount
           * are already set up.
           */
!         osi_Log2(smb_logp, "SMB received op 0x%x lsn %d", inp->inCom,
!                   ncbp->ncb_lsn);
  
!         /* now do the dispatch */
!         /* start by formatting the response record a little, as a default */
          if (dp->flags & SMB_DISPATCHFLAG_CHAINED) {
!             outWctp[0] = 2;
              outWctp[1] = 0xff;	/* no operation */
              outWctp[2] = 0;		/* padding */
              outWctp[3] = 0;
              outWctp[4] = 0;
          }
!         else {
!             /* not a chained request, this is a more reasonable default */
              outWctp[0] = 0;	/* wct of zero */
              outWctp[1] = 0;	/* and bcc (word) of zero */
              outWctp[2] = 0;
!         }   
  
!         /* once set, stays set.  Doesn't matter, since we never chain
           * "no response" calls.
           */
!         if (dp->flags & SMB_DISPATCHFLAG_NORESPONSE)
              noSend = 1;
  
          if (dp->procp) {
!             /* we have a recognized operation */
  
!             if (inp->inCom == 0x1d)
!                 /* Raw Write */
!                 code = smb_ReceiveCoreWriteRaw (vcp, inp, outp,
!                                                  rwcp);
!             else {
!                 osi_LogEvent("AFS Dispatch %s",(myCrt_Dispatch(inp->inCom)),"vcp[%x] lana[%d] lsn[%d]",(int)vcp,vcp->lana,vcp->lsn);
!                 osi_Log4(smb_logp,"Dispatch %s vcp[%x] lana[%d] lsn[%d]",(myCrt_Dispatch(inp->inCom)),vcp,vcp->lana,vcp->lsn);
!                 code = (*(dp->procp)) (vcp, inp, outp);
!                 osi_LogEvent("AFS Dispatch return",NULL,"Code[%d]",(code==0)?0:code-CM_ERROR_BASE);
!                 osi_Log1(smb_logp,"Dispatch return  code[%d]",(code==0)?0:code-CM_ERROR_BASE);
! #ifdef LOG_PACKET
!                 if ( code == CM_ERROR_BADSMB ||
!                      code == CM_ERROR_BADOP )
!                 smb_LogPacket(inp);
! #endif /* LOG_PACKET */
!             }   
! 
!             if (oldGen != sessionGen) {
! #ifndef DJGPP
!                 HANDLE h;
!                 char *ptbuf[1];
!                 char s[100];
!                 newTime = GetCurrentTime();
!                 h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
!                 sprintf(s, "Pkt straddled session startup, took %d ms, ncb length %d",
!                          newTime - oldTime, ncbp->ncb_length);
!                 ptbuf[0] = s;
!                 ReportEvent(h, EVENTLOG_WARNING_TYPE, 0,
!                              1005, NULL, 1, ncbp->ncb_length, ptbuf, smbp);
!                 DeregisterEventSource(h);
  #endif /* !DJGPP */
!                 osi_Log1(smb_logp, "Pkt straddled session startup, "
!                           "ncb length %d", ncbp->ncb_length);
!             }
          }
          else {
!             /* bad opcode, fail the request, after displaying it */
!             osi_Log1(smb_logp, "Received bad SMB req 0x%X", inp->inCom);
! #ifdef LOG_PACKET
              smb_LogPacket(inp);
! #endif  /* LOG_PACKET */
  
  #ifndef DJGPP
!             if (showErrors) {
!                 sprintf(tbuffer, "Received bad SMB req 0x%x", inp->inCom);
                  code = (*smb_MBfunc)(NULL, tbuffer, "Cancel: don't show again",
!                                       MB_OKCANCEL|MB_SERVICE_NOTIFICATION);
!                 if (code == IDCANCEL) 
!                     showErrors = 0;
!             }
  #endif /* DJGPP */
              code = CM_ERROR_BADOP;
          }
  
!         /* catastrophic failure:  log as much as possible */
!         if (code == CM_ERROR_BADSMB) {
  #ifndef DJGPP
!             HANDLE h;
!             char *ptbuf[1];
!             char s[100];
  
!             osi_Log1(smb_logp,
                        "Invalid SMB, ncb_length %d",
                        ncbp->ncb_length);
  
!             h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
!             sprintf(s, "Invalid SMB message, length %d",
                       ncbp->ncb_length);
!             ptbuf[0] = s;
!             ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, 1002, NULL,
                           1, ncbp->ncb_length, ptbuf, smbp);
!             DeregisterEventSource(h);
! #ifdef LOG_PACKET
              smb_LogPacket(inp);
! #endif /* LOG_PACKET */
  #endif /* !DJGPP */
              osi_Log1(smb_logp, "Invalid SMB message, length %d",
                       ncbp->ncb_length);
  
!             code = CM_ERROR_INVAL;
!         }
  
!         if (outp->flags & SMB_PACKETFLAG_NOSEND) {
!             thrd_Decrement(&ongoingOps);
!             return;
!         }
  
        resume:
!         /* now, if we failed, turn the current response into an empty
           * one, and fill in the response packet's error code.
           */
!         if (code) {
!             if (vcp->flags & SMB_VCFLAG_STATUS32) {
!                 smb_MapNTError(code, &NTStatus);
!                 outWctp = outp->wctp;
!                 smbp = (smb_t *) &outp->data;
!                 if (code != CM_ERROR_PARTIALWRITE
!                      && code != CM_ERROR_BUFFERTOOSMALL 
!                      && code != CM_ERROR_GSSCONTINUE) {
!                     /* nuke wct and bcc.  For a partial
!                      * write or an in-process authentication handshake, 
                       * assume they're OK.
!                      */
!                     *outWctp++ = 0;
!                     *outWctp++ = 0;
!                     *outWctp++ = 0;
!                 }
!                 smbp->rcls = (unsigned char) (NTStatus & 0xff);
!                 smbp->reh = (unsigned char) ((NTStatus >> 8) & 0xff);
!                 smbp->errLow = (unsigned char) ((NTStatus >> 16) & 0xff);
!                 smbp->errHigh = (unsigned char) ((NTStatus >> 24) & 0xff);
!                 smbp->flg2 |= 0x4000;
!                 break;
!             }
!             else {
                  smb_MapCoreError(code, vcp, &errCode, &errClass);
!                 outWctp = outp->wctp;
!                 smbp = (smb_t *) &outp->data;
!                 if (code != CM_ERROR_PARTIALWRITE) {
!                     /* nuke wct and bcc.  For a partial
!                      * write, assume they're OK.
!                      */
!                     *outWctp++ = 0;
!                     *outWctp++ = 0;
!                     *outWctp++ = 0;
!                 }
!                 smbp->errLow = (unsigned char) (errCode & 0xff);
!                 smbp->errHigh = (unsigned char) ((errCode >> 8) & 0xff);
                  smbp->rcls = errClass;
!                 break;
!             }
!         }	/* error occurred */
! 
          /* if we're here, we've finished one request.  Look to see if
!          * this is a chained opcode.  If it is, setup things to process
!          * the chained request, and setup the output buffer to hold the
!          * chained response.  Start by finding the next input record.
           */
          if (!(dp->flags & SMB_DISPATCHFLAG_CHAINED))
              break;		/* not a chained req */
***************
*** 6168,6174 ****
  
          /* and now append the next output request to the end of this
           * last request.  Begin by finding out where the last response
! 		 * ends, since that's where we'll put our new response.
           */
          outWctp = outp->wctp;		/* ptr to out parameters */
          osi_assert (outWctp[0] >= 2);	/* need this for all chained requests */
--- 6361,6367 ----
  
          /* and now append the next output request to the end of this
           * last request.  Begin by finding out where the last response
!          * ends, since that's where we'll put our new response.
           */
          outWctp = outp->wctp;		/* ptr to out parameters */
          osi_assert (outWctp[0] >= 2);	/* need this for all chained requests */
***************
*** 6176,6210 ****
          tp = outWctp + nparms + 1;	/* now points to bcc field */
          nbytes = tp[0] + (tp[1] << 8);	/* # of data bytes */
          tp += 2 /* for the count itself */ + nbytes;
! 		/* tp now points to the new output record; go back and patch the
           * second parameter (off2) to point to the new record.
           */
! 		temp = (unsigned int)tp - ((unsigned int) outp->data);
          outWctp[3] = (unsigned char) (temp & 0xff);
          outWctp[4] = (unsigned char) ((temp >> 8) & 0xff);
          outWctp[2] = 0;	/* padding */
          outWctp[1] = inp->inCom;	/* next opcode */
  
! 		/* finally, setup for the next iteration */
          outp->wctp = tp;
! 		outWctp = tp;
! 	}	/* while loop over all requests in the packet */
  
! 	/* done logging out, turn off logging-out flag */
! 	if (!(inp->flags & SMB_PACKETFLAG_PROFILE_UPDATE_OK)) {
! 		vcp->justLoggedOut = NULL;
! 		if (loggedOut) {
! 			loggedOut = 0;
! 			free(loggedOutName);
! 			loggedOutName = NULL;
! 			smb_ReleaseUID(loggedOutUserp);
! 			loggedOutUserp = NULL;
! 		}
! 	}
   
      /* now send the output packet, and return */
      if (!noSend)
! 		smb_SendPacket(vcp, outp);
  	thrd_Decrement(&ongoingOps);
  
  	if (!(vcp->flags & SMB_VCFLAG_ALREADYDEAD)) {
--- 6369,6403 ----
          tp = outWctp + nparms + 1;	/* now points to bcc field */
          nbytes = tp[0] + (tp[1] << 8);	/* # of data bytes */
          tp += 2 /* for the count itself */ + nbytes;
!         /* tp now points to the new output record; go back and patch the
           * second parameter (off2) to point to the new record.
           */
!         temp = (unsigned int)tp - ((unsigned int) outp->data);
          outWctp[3] = (unsigned char) (temp & 0xff);
          outWctp[4] = (unsigned char) ((temp >> 8) & 0xff);
          outWctp[2] = 0;	/* padding */
          outWctp[1] = inp->inCom;	/* next opcode */
  
!         /* finally, setup for the next iteration */
          outp->wctp = tp;
!         outWctp = tp;
!     }	/* while loop over all requests in the packet */
  
!     /* done logging out, turn off logging-out flag */
!     if (!(inp->flags & SMB_PACKETFLAG_PROFILE_UPDATE_OK)) {
!         vcp->justLoggedOut = NULL;
!         if (loggedOut) {
!             loggedOut = 0;
!             free(loggedOutName);
!             loggedOutName = NULL;
!             smb_ReleaseUID(loggedOutUserp);
!             loggedOutUserp = NULL;
!         }
!     }
   
      /* now send the output packet, and return */
      if (!noSend)
!         smb_SendPacket(vcp, outp);
  	thrd_Decrement(&ongoingOps);
  
  	if (!(vcp->flags & SMB_VCFLAG_ALREADYDEAD)) {
***************
*** 6214,6225 ****
                        "Replacing active_vcp %x with %x", active_vcp, vcp);
          }
          smb_HoldVC(vcp);
! 		active_vcp = vcp;
! 		last_msg_time = GetCurrentTime();
! 	}
  	else if (active_vcp == vcp) {
!         smb_ReleaseVC(active_vcp);
! 		active_vcp = NULL;
      }
  
      return;
--- 6407,6418 ----
                        "Replacing active_vcp %x with %x", active_vcp, vcp);
          }
          smb_HoldVC(vcp);
!             active_vcp = vcp;
!             last_msg_time = GetCurrentTime();
! 	}       
  	else if (active_vcp == vcp) {
!             smb_ReleaseVC(active_vcp);
!             active_vcp = NULL;
      }
  
      return;
***************
*** 6233,6246 ****
   */
  void smb_ClientWaiter(void *parmp)
  {
! 	DWORD code;
      int   idx;
  
! 	while (1) {
! 		code = thrd_WaitForMultipleObjects_Event(numNCBs, NCBevents,
! 					      FALSE, INFINITE);
! 		if (code == WAIT_OBJECT_0)
! 			continue;
  
          /* error checking */
          if (code >= WAIT_ABANDONED_0 && code < (WAIT_ABANDONED_0 + numNCBs))
--- 6426,6439 ----
   */
  void smb_ClientWaiter(void *parmp)
  {
!     DWORD code;
      int   idx;
  
!     while (1) {
!         code = thrd_WaitForMultipleObjects_Event(numNCBs, NCBevents,
!                                                  FALSE, INFINITE);
!         if (code == WAIT_OBJECT_0)
!             continue;
  
          /* error checking */
          if (code >= WAIT_ABANDONED_0 && code < (WAIT_ABANDONED_0 + numNCBs))
***************
*** 6275,6283 ****
              osi_assert(0);
          }
          
! 		thrd_ResetEvent(NCBevents[idx]);
! 		thrd_SetEvent(NCBreturns[0][idx]);
! 	}
  }
  #endif /* !DJGPP */
  
--- 6468,6476 ----
              osi_assert(0);
          }
          
!         thrd_ResetEvent(NCBevents[idx]);
!         thrd_SetEvent(NCBreturns[0][idx]);
!     }
  }
  #endif /* !DJGPP */
  
***************
*** 6287,6305 ****
   */
  void smb_ServerWaiter(void *parmp)
  {
! 	DWORD code;
      int idx_session, idx_NCB;
! 	NCB *ncbp;
  #ifdef DJGPP
      dos_ptr dos_ncb;
  #endif /* DJGPP */
  
! 	while (1) {
! 		/* Get a session */
! 		code = thrd_WaitForMultipleObjects_Event(numSessions, SessionEvents,
!                                                    FALSE, INFINITE);
! 		if (code == WAIT_OBJECT_0)
! 			continue;
  
          if (code >= WAIT_ABANDONED_0 && code < (WAIT_ABANDONED_0 + numSessions))
          {
--- 6480,6498 ----
   */
  void smb_ServerWaiter(void *parmp)
  {
!     DWORD code;
      int idx_session, idx_NCB;
!     NCB *ncbp;
  #ifdef DJGPP
      dos_ptr dos_ncb;
  #endif /* DJGPP */
  
!     while (1) {
!         /* Get a session */
!         code = thrd_WaitForMultipleObjects_Event(numSessions, SessionEvents,
!                                                  FALSE, INFINITE);
!         if (code == WAIT_OBJECT_0)
!             continue;
  
          if (code >= WAIT_ABANDONED_0 && code < (WAIT_ABANDONED_0 + numSessions))
          {
***************
*** 6335,6344 ****
  
  		/* Get an NCB */
        NCBretry:
! 		code = thrd_WaitForMultipleObjects_Event(numNCBs, NCBavails,
!                                                    FALSE, INFINITE);
! 		if (code == WAIT_OBJECT_0)
! 			goto NCBretry;
  
          /* error checking */
          if (code >= WAIT_ABANDONED_0 && code < (WAIT_ABANDONED_0 + numNCBs))
--- 6528,6537 ----
  
  		/* Get an NCB */
        NCBretry:
!         code = thrd_WaitForMultipleObjects_Event(numNCBs, NCBavails,
!                                                  FALSE, INFINITE);
!         if (code == WAIT_OBJECT_0)
!             goto NCBretry;
  
          /* error checking */
          if (code >= WAIT_ABANDONED_0 && code < (WAIT_ABANDONED_0 + numNCBs))
***************
*** 6373,6402 ****
              osi_assert(0);
          }
  
! 		/* Link them together */
! 		NCBsessions[idx_NCB] = idx_session;
  
! 		/* Fire it up */
! 		ncbp = NCBs[idx_NCB];
  #ifdef DJGPP
          dos_ncb = ((smb_ncb_t *)ncbp)->dos_ncb;
  #endif /* DJGPP */
! 		ncbp->ncb_lsn = (unsigned char) LSNs[idx_session];
! 		ncbp->ncb_command = NCBRECV | ASYNCH;
! 		ncbp->ncb_lana_num = lanas[idx_session];
! #ifndef DJGPP
! 		ncbp->ncb_buffer = (unsigned char *) bufs[idx_NCB];
! 		ncbp->ncb_event = NCBevents[idx_NCB];
! 		ncbp->ncb_length = SMB_PACKETSIZE;
! 		Netbios(ncbp);
  #else /* DJGPP */
! 		ncbp->ncb_buffer = bufs[idx_NCB]->dos_pkt;
!                 ((smb_ncb_t*)ncbp)->orig_pkt = bufs[idx_NCB];
! 		ncbp->ncb_event = NCBreturns[0][idx_NCB];
! 		ncbp->ncb_length = SMB_PACKETSIZE;
! 		Netbios(ncbp, dos_ncb);
  #endif /* !DJGPP */
! 	}
  }
  
  /*
--- 6566,6595 ----
              osi_assert(0);
          }
  
!         /* Link them together */
!         NCBsessions[idx_NCB] = idx_session;
  
!         /* Fire it up */
!         ncbp = NCBs[idx_NCB];
  #ifdef DJGPP
          dos_ncb = ((smb_ncb_t *)ncbp)->dos_ncb;
  #endif /* DJGPP */
!         ncbp->ncb_lsn = (unsigned char) LSNs[idx_session];
!         ncbp->ncb_command = NCBRECV | ASYNCH;
!         ncbp->ncb_lana_num = lanas[idx_session];
! #ifndef DJGPP
!         ncbp->ncb_buffer = (unsigned char *) bufs[idx_NCB];
!         ncbp->ncb_event = NCBevents[idx_NCB];
!         ncbp->ncb_length = SMB_PACKETSIZE;
!         Netbios(ncbp);
  #else /* DJGPP */
!         ncbp->ncb_buffer = bufs[idx_NCB]->dos_pkt;
!         ((smb_ncb_t*)ncbp)->orig_pkt = bufs[idx_NCB];
!         ncbp->ncb_event = NCBreturns[0][idx_NCB];
!         ncbp->ncb_length = SMB_PACKETSIZE;
!         Netbios(ncbp, dos_ncb);
  #endif /* !DJGPP */
!     }
  }
  
  /*
***************
*** 6411,6452 ****
   */
  void smb_Server(VOID *parmp)
  {
! 	int myIdx = (int) parmp;
! 	NCB *ncbp;
! 	NCB *outncbp;
      smb_packet_t *bufp;
! 	smb_packet_t *outbufp;
      DWORD code, rcode;
      int idx_NCB, idx_session;
! 	UCHAR rc;
! 	smb_vc_t *vcp = NULL;
! 	smb_t *smbp;
  #ifdef DJGPP
      dos_ptr dos_ncb;
  #endif /* DJGPP */
  
! 	outncbp = GetNCB();
! 	outbufp = GetPacket();
! 	outbufp->ncbp = outncbp;
! 
! 	while (1) {
! #ifndef NOEXPIRE
! 		/* check for demo expiration */
! 		{
! 			unsigned long tod = time((void *) 0);
! 			if (tod > EXPIREDATE) {
! 				(*smb_MBfunc)(NULL, "AFS demo expiration",
! 					   "afsd dispatcher",
! 					   MB_OK|MB_ICONSTOP|MB_SETFOREGROUND|MB_SERVICE_NOTIFICATION);
! 				trhd_Exit(1);
! 			}
! 		}
! #endif /* !NOEXPIRE */
! 
! 		code = thrd_WaitForMultipleObjects_Event(numNCBs, NCBreturns[myIdx],
!                                                    FALSE, INFINITE);
! 		if (code == WAIT_OBJECT_0) {
! 			continue;
          }
  
          /* error checking */
--- 6604,6632 ----
   */
  void smb_Server(VOID *parmp)
  {
!     int myIdx = (int) parmp;
!     NCB *ncbp;
!     NCB *outncbp;
      smb_packet_t *bufp;
!     smb_packet_t *outbufp;
      DWORD code, rcode;
      int idx_NCB, idx_session;
!     UCHAR rc;
!     smb_vc_t *vcp = NULL;
!     smb_t *smbp;
  #ifdef DJGPP
      dos_ptr dos_ncb;
  #endif /* DJGPP */
  
!     outncbp = GetNCB();
!     outbufp = GetPacket();
!     outbufp->ncbp = outncbp;
! 
!     while (1) {
!         code = thrd_WaitForMultipleObjects_Event(numNCBs, NCBreturns[myIdx],
!                                                  FALSE, INFINITE);
!         if (code == WAIT_OBJECT_0) {
!             continue;
          }
  
          /* error checking */
***************
*** 6482,6664 ****
              osi_assert(0);
          }
  
! 		ncbp = NCBs[idx_NCB];
  #ifdef DJGPP
!                 dos_ncb = ((smb_ncb_t *)ncbp)->dos_ncb;
  #endif /* DJGPP */
! 		idx_session = NCBsessions[idx_NCB];
! 		rc = ncbp->ncb_retcode;
  
! 		if (rc != NRC_PENDING && rc != NRC_GOODRET)
! 			osi_Log1(smb_logp, "NCBRECV failure code %d", rc);
  
! 		switch (rc) {
! 			case NRC_GOODRET: break;
  
! 			case NRC_PENDING:
! 				/* Can this happen? Or is it just my
! 				 * UNIX paranoia? 
!                  */
! 				continue;
  
! 			case NRC_SCLOSED:
! 			case NRC_SNUMOUT:
! 				/* Client closed session */
!                 if (reportSessionStartups) 
!                 {
!                     osi_Log1(smb_logp, "session [ %d ] closed", idx_session);
                  }
! 				dead_sessions[idx_session] = TRUE;
!                 if (vcp)
!                     smb_ReleaseVC(vcp);
! 				vcp = smb_FindVC(ncbp->ncb_lsn, 0, lanas[idx_session]);
! 				/* Should also release vcp.  [done] 2004-05-11 jaltman
!                  * Also, should do
! 				 * sanity check that all TID's are gone. 
!                  *
!                  * TODO: check if we could use LSNs[idx_session] instead, 
!                  * also cleanup after dead vcp 
!                  */
!                 if (vcp) {
!                     if (dead_vcp)
!                         osi_Log1(smb_logp,
!                                   "dead_vcp already set, %x",
!                                   dead_vcp);
!                     if (!dead_vcp && !(vcp->flags & SMB_VCFLAG_ALREADYDEAD)) {
!                         osi_Log2(smb_logp,
!                                   "setting dead_vcp %x, user struct %x",
!                                   vcp, vcp->usersp);
!                         smb_HoldVC(vcp);
!                         dead_vcp = vcp;
!                         vcp->flags |= SMB_VCFLAG_ALREADYDEAD;
!                     }
!                     if (vcp->justLoggedOut) {
!                         loggedOut = 1;
!                         loggedOutTime = vcp->logoffTime;
!                         loggedOutName =
!                             strdup(vcp->justLoggedOut->unp->name);
!                         loggedOutUserp = vcp->justLoggedOut;
!                         lock_ObtainWrite(&smb_rctLock);
!                         loggedOutUserp->refCount++;
!                         lock_ReleaseWrite(&smb_rctLock);
!                     }
                  }
! 				goto doneWithNCB;
  
! 			case NRC_INCOMP:
! 				/* Treat as transient error */
! 				{
! #ifndef DJGPP
! 					EVENT_HANDLE h;
! 					char *ptbuf[1];
! 					char s[100];
! 
! 					h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
! 					sprintf(s, "SMB message incomplete, length %d",
!                             ncbp->ncb_length);
! 					ptbuf[0] = s;
! 					ReportEvent(h, EVENTLOG_WARNING_TYPE, 0,
!                                 1001, NULL, 1,
!                                 ncbp->ncb_length, ptbuf,
!                                 bufp);
! 					DeregisterEventSource(h);
  #endif /* !DJGPP */
! 					osi_Log1(smb_logp,
!                               "dispatch smb recv failed, message incomplete, ncb_length %d",
!                               ncbp->ncb_length);
!                     osi_Log1(smb_logp,
!                               "SMB message incomplete, "
!                               "length %d", ncbp->ncb_length);
  
! 					/*
! 					 * We used to discard the packet.
! 					 * Instead, try handling it normally.
! 					 *
!                      continue;
! 		 			 */
! 					break;
! 				}
! 
! 			default:
! 				/* A weird error code.  Log it, sleep, and
! 				 * continue. */
! 				if (vcp && vcp->errorCount++ > 3) {
!                     osi_Log2(smb_logp, "session [ %d ] closed, vcp->errorCount = %d", idx_session, vcp->errorCount);
! 					dead_sessions[idx_session] = TRUE;
!                 }
! 				else {
! 					thrd_Sleep(1000);
! 					thrd_SetEvent(SessionEvents[idx_session]);
! 				}
! 				continue;
! 		}
! 
! 		/* Success, so now dispatch on all the data in the packet */
! 
! 		smb_concurrentCalls++;
! 		if (smb_concurrentCalls > smb_maxObsConcurrentCalls)
! 			smb_maxObsConcurrentCalls = smb_concurrentCalls;
  
          if (vcp)
              smb_ReleaseVC(vcp);
! 		vcp = smb_FindVC(ncbp->ncb_lsn, 0, ncbp->ncb_lana_num);
          /*
! 		* If at this point vcp is NULL (implies that packet was invalid)
! 		* then we are in big trouble. This means either :
! 		*   a) we have the wrong NCB.
! 		*   b) Netbios screwed up the call.
! 		* Obviously this implies that 
! 		*   ( LSNs[idx_session] != ncbp->ncb_lsn ||
! 		*   lanas[idx_session] != ncbp->ncb_lana_num )
! 		* Either way, we can't do anything with this packet.
! 		* Log, sleep and resume.
! 		*/
! 		if(!vcp) {
! 			HANDLE h;
! 			char buf[1000];
! 			char *ptbuf[1];
! 
! 			sprintf(buf,
! 				"Bad vcp!! : "
! 				"LSNs[idx_session]=[%d],"
! 				"lanas[idx_session]=[%d],"
! 				"ncbp->ncb_lsn=[%d],"
! 				"ncbp->ncb_lana_num=[%d]",
! 				LSNs[idx_session],
! 				lanas[idx_session],
! 				ncbp->ncb_lsn,
! 				ncbp->ncb_lana_num);
! 
! 			ptbuf[0] = buf;
! 
! 			h = RegisterEventSource(NULL,AFS_DAEMON_EVENT_NAME);
! 			if(h) {
! 				ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, 1001, NULL,1,sizeof(*ncbp),ptbuf,(void*)ncbp);
! 				DeregisterEventSource(h);
! 			}
! 
! 			/* Also log in the trace log. */
! 			osi_Log4(smb_logp, "Server: BAD VCP!"
! 				"LSNs[idx_session]=[%d],"
! 				"lanas[idx_session]=[%d],"
! 				"ncbp->ncb_lsn=[%d],"
! 				"ncbp->ncb_lana_num=[%d]",
! 				LSNs[idx_session],
! 				lanas[idx_session],
! 				ncbp->ncb_lsn,
! 				ncbp->ncb_lana_num);
! 
! 			/* thrd_Sleep(1000); Don't bother sleeping */
! 			thrd_SetEvent(SessionEvents[idx_session]);
! 			smb_concurrentCalls--;
! 			continue;
! 		}
  
  
! 		vcp->errorCount = 0;
! 		bufp = (struct smb_packet *) ncbp->ncb_buffer;
  #ifdef DJGPP
! 		bufp = ((smb_ncb_t *) ncbp)->orig_pkt;
          /* copy whole packet to virtual memory */
          /*fprintf(stderr, "smb_Server: copying dos packet at 0x%x, "
          "bufp=0x%x\n",
--- 6662,6843 ----
              osi_assert(0);
          }
  
!         ncbp = NCBs[idx_NCB];
  #ifdef DJGPP
!         dos_ncb = ((smb_ncb_t *)ncbp)->dos_ncb;
  #endif /* DJGPP */
!         idx_session = NCBsessions[idx_NCB];
!         rc = ncbp->ncb_retcode;
  
!         if (rc != NRC_PENDING && rc != NRC_GOODRET)
!             osi_Log1(smb_logp, "NCBRECV failure code %d", rc);
  
!         switch (rc) {
!         case NRC_GOODRET: break;
  
!         case NRC_PENDING:
!             /* Can this happen? Or is it just my
!              * UNIX paranoia? 
!              */
!             continue;
  
!         case NRC_SCLOSED:
!         case NRC_SNUMOUT:
!             /* Client closed session */
!             if (reportSessionStartups) 
!             {
!                 osi_Log1(smb_logp, "session [ %d ] closed", idx_session);
!             }
!             dead_sessions[idx_session] = TRUE;
!             if (vcp)
!                 smb_ReleaseVC(vcp);
!             vcp = smb_FindVC(ncbp->ncb_lsn, 0, lanas[idx_session]);
!             /* Should also release vcp.  [done] 2004-05-11 jaltman
!              * Also, should do
!              * sanity check that all TID's are gone. 
!              *
!              * TODO: check if we could use LSNs[idx_session] instead, 
!              * also cleanup after dead vcp 
!              */
!             if (vcp) {
!                 if (dead_vcp)
!                     osi_Log1(smb_logp,
!                              "dead_vcp already set, %x",
!                              dead_vcp);
!                 if (!dead_vcp && !(vcp->flags & SMB_VCFLAG_ALREADYDEAD)) {
!                     osi_Log2(smb_logp,
!                              "setting dead_vcp %x, user struct %x",
!                              vcp, vcp->usersp);
!                     smb_HoldVC(vcp);
!                     dead_vcp = vcp;
!                     vcp->flags |= SMB_VCFLAG_ALREADYDEAD;
                  }
!                 if (vcp->justLoggedOut) {
!                     loggedOut = 1;
!                     loggedOutTime = vcp->logoffTime;
!                     loggedOutName = strdup(vcp->justLoggedOut->unp->name);
!                     loggedOutUserp = vcp->justLoggedOut;
!                     lock_ObtainWrite(&smb_rctLock);
!                     loggedOutUserp->refCount++;
!                     lock_ReleaseWrite(&smb_rctLock);
                  }
!             }
!             goto doneWithNCB;
  
!         case NRC_INCOMP:
!             /* Treat as transient error */
!             {
! #ifndef DJGPP
!                 EVENT_HANDLE h;
!                 char *ptbuf[1];
!                 char s[100];
! 
!                 h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
!                 sprintf(s, "SMB message incomplete, length %d",
!                          ncbp->ncb_length);
!                 ptbuf[0] = s;
!                 ReportEvent(h, EVENTLOG_WARNING_TYPE, 0,
!                              1001, NULL, 1,
!                              ncbp->ncb_length, ptbuf,
!                              bufp);
!                 DeregisterEventSource(h);
  #endif /* !DJGPP */
!                 osi_Log1(smb_logp,
!                           "dispatch smb recv failed, message incomplete, ncb_length %d",
!                           ncbp->ncb_length);
!                 osi_Log1(smb_logp,
!                           "SMB message incomplete, "
!                           "length %d", ncbp->ncb_length);
! 
!                 /*
!                  * We used to discard the packet.
!                  * Instead, try handling it normally.
!                  *
!                  continue;
!                  */
!                 break;
!             }
  
!         default:
!             /* A weird error code.  Log it, sleep, and
!             * continue. */
!             if (vcp && vcp->errorCount++ > 3) {
!                 osi_Log2(smb_logp, "session [ %d ] closed, vcp->errorCount = %d", idx_session, vcp->errorCount);
!                 dead_sessions[idx_session] = TRUE;
!             }
!             else {
!                 thrd_Sleep(1000);
!                 thrd_SetEvent(SessionEvents[idx_session]);
!             }
!             continue;
!         }
! 
!         /* Success, so now dispatch on all the data in the packet */
! 
!         smb_concurrentCalls++;
!         if (smb_concurrentCalls > smb_maxObsConcurrentCalls)
!             smb_maxObsConcurrentCalls = smb_concurrentCalls;
  
          if (vcp)
              smb_ReleaseVC(vcp);
!         vcp = smb_FindVC(ncbp->ncb_lsn, 0, ncbp->ncb_lana_num);
          /*
!          * If at this point vcp is NULL (implies that packet was invalid)
!          * then we are in big trouble. This means either :
!          *   a) we have the wrong NCB.
!          *   b) Netbios screwed up the call.
!          * Obviously this implies that 
!          *   ( LSNs[idx_session] != ncbp->ncb_lsn ||
!          *   lanas[idx_session] != ncbp->ncb_lana_num )
!          * Either way, we can't do anything with this packet.
!          * Log, sleep and resume.
!          */
!         if(!vcp) {
!             HANDLE h;
!             char buf[1000];
!             char *ptbuf[1];
! 
!             sprintf(buf,
!                      "Bad vcp!! : "
!                      "LSNs[idx_session]=[%d],"
!                      "lanas[idx_session]=[%d],"
!                      "ncbp->ncb_lsn=[%d],"
!                      "ncbp->ncb_lana_num=[%d]",
!                      LSNs[idx_session],
!                      lanas[idx_session],
!                      ncbp->ncb_lsn,
!                      ncbp->ncb_lana_num);
! 
!             ptbuf[0] = buf;
! 
!             h = RegisterEventSource(NULL,AFS_DAEMON_EVENT_NAME);
!             if(h) {
!                 ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, 1001, NULL,1,sizeof(*ncbp),ptbuf,(void*)ncbp);
!                 DeregisterEventSource(h);
!             }
  
+             /* Also log in the trace log. */
+             osi_Log4(smb_logp, "Server: BAD VCP!"
+                       "LSNs[idx_session]=[%d],"
+                       "lanas[idx_session]=[%d],"
+                       "ncbp->ncb_lsn=[%d],"
+                       "ncbp->ncb_lana_num=[%d]",
+                       LSNs[idx_session],
+                       lanas[idx_session],
+                       ncbp->ncb_lsn,
+                       ncbp->ncb_lana_num);
+ 
+             /* thrd_Sleep(1000); Don't bother sleeping */
+             thrd_SetEvent(SessionEvents[idx_session]);
+             smb_concurrentCalls--;
+             continue;
+         }
  
! 
!         vcp->errorCount = 0;
!         bufp = (struct smb_packet *) ncbp->ncb_buffer;
  #ifdef DJGPP
!         bufp = ((smb_ncb_t *) ncbp)->orig_pkt;
          /* copy whole packet to virtual memory */
          /*fprintf(stderr, "smb_Server: copying dos packet at 0x%x, "
          "bufp=0x%x\n",
***************
*** 6666,6729 ****
          fflush(stderr);
          dosmemget(bufp->dos_pkt, ncbp->ncb_length, bufp->data);
  #endif /* DJGPP */
! 		smbp = (smb_t *)bufp->data;
! 		outbufp->flags = 0;
  
  #if !defined(DJGPP) && !defined(AFS_WIN32_ENV)
          __try
          {
  #endif
! 		if (smbp->com == 0x1d) {
! 			/* Special handling for Write Raw */
! 			raw_write_cont_t rwc;
! 			EVENT_HANDLE rwevent;
!             char eventName[MAX_PATH];
              
!             smb_DispatchPacket(vcp, bufp, outbufp, ncbp, &rwc);
! 			if (rwc.code == 0) {
! 				rwevent = thrd_CreateEvent(NULL, FALSE, FALSE, TEXT("smb_Server() rwevent"));
!                 if ( GetLastError() == ERROR_ALREADY_EXISTS )
!  