windows-largefile-support-20060623
[openafs.git] / src / WINNT / afsd / cm_conn.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afs/param.h>
11 #include <afs/stds.h>
12
13 #ifndef DJGPP
14 #include <windows.h>
15 #endif /* !DJGPP */
16 #include <string.h>
17 #include <malloc.h>
18 #include <osi.h>
19 #include "afsd.h"
20 #include <rx/rx.h>
21 #include <rx/rxkad.h>
22 #include <afs/unified_afs.h>
23
24 osi_rwlock_t cm_connLock;
25
26 long RDRtimeout = CM_CONN_DEFAULTRDRTIMEOUT;
27 unsigned short ConnDeadtimeout = CM_CONN_CONNDEADTIME;
28 unsigned short HardDeadtimeout = CM_CONN_HARDDEADTIME;
29
30 #define LANMAN_WKS_PARAM_KEY "SYSTEM\\CurrentControlSet\\Services\\lanmanworkstation\\parameters"
31 #define LANMAN_WKS_SESSION_TIMEOUT "SessTimeout"
32
33 afs_int32 cryptall = 0;
34
35 void cm_PutConn(cm_conn_t *connp)
36 {
37         lock_ObtainWrite(&cm_connLock);
38         osi_assert(connp->refCount-- > 0);
39         lock_ReleaseWrite(&cm_connLock);
40 }
41
42 void cm_InitConn(void)
43 {
44         static osi_once_t once;
45         long code;
46         DWORD sessTimeout;
47         HKEY parmKey;
48         
49     if (osi_Once(&once)) {
50                 lock_InitializeRWLock(&cm_connLock, "connection global lock");
51
52         /* keisa - read timeout value for lanmanworkstation  service.
53          * jaltman - as per 
54          *   http://support.microsoft.com:80/support/kb/articles/Q102/0/67.asp&NoWebContent=1
55          * the SessTimeout is a minimum timeout not a maximum timeout.  Therefore, 
56          * I believe that the default should not be short.  Instead, we should wait until
57          * RX times out before reporting a timeout to the SMB client.
58          */
59                 code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, LANMAN_WKS_PARAM_KEY,
60                             0, KEY_QUERY_VALUE, &parmKey);
61                 if (code == ERROR_SUCCESS)
62         {
63                     DWORD dummyLen = sizeof(sessTimeout);
64                     code = RegQueryValueEx(parmKey, LANMAN_WKS_SESSION_TIMEOUT, NULL, NULL, 
65                                    (BYTE *) &sessTimeout, &dummyLen);
66                     if (code == ERROR_SUCCESS)
67             {
68                 afsi_log("lanmanworkstation : SessTimeout %d", sessTimeout);
69                 RDRtimeout = sessTimeout;
70                 if ( ConnDeadtimeout < RDRtimeout + 15 ) {
71                     ConnDeadtimeout = RDRtimeout + 15;
72                     afsi_log("ConnDeadTimeout increased to %d", ConnDeadtimeout);
73                 }
74                 if ( HardDeadtimeout < 2 * ConnDeadtimeout ) {
75                     HardDeadtimeout = 2 * ConnDeadtimeout;
76                     afsi_log("HardDeadTimeout increased to %d", HardDeadtimeout);
77                 }
78             }
79         }
80
81         osi_EndOnce(&once);
82     }
83 }
84
85 void cm_InitReq(cm_req_t *reqp)
86 {
87         memset((char *)reqp, 0, sizeof(cm_req_t));
88 #ifndef DJGPP
89         reqp->startTime = GetTickCount();
90 #else
91         gettimeofday(&reqp->startTime, NULL);
92 #endif
93 }
94
95 static long cm_GetServerList(struct cm_fid *fidp, struct cm_user *userp,
96         struct cm_req *reqp, cm_serverRef_t ***serversppp)
97 {
98     long code;
99     cm_volume_t *volp = NULL;
100     cm_cell_t *cellp = NULL;
101
102     if (!fidp) {
103         *serversppp = NULL;
104         return 0;
105     }
106
107     cellp = cm_FindCellByID(fidp->cell);
108     if (!cellp) return CM_ERROR_NOSUCHCELL;
109
110     code = cm_GetVolumeByID(cellp, fidp->volume, userp, reqp, &volp);
111     if (code) return code;
112     
113     *serversppp = cm_GetVolServers(volp, fidp->volume);
114
115     cm_PutVolume(volp);
116     return 0;
117 }
118
119 /*
120  * Analyze the error return from an RPC.  Determine whether or not to retry,
121  * and if we're going to retry, determine whether failover is appropriate,
122  * and whether timed backoff is appropriate.
123  *
124  * If the error code is from cm_Conn() or friends, it will be a CM_ERROR code.
125  * Otherwise it will be an RPC code.  This may be a UNIX code (e.g. EDQUOT), or
126  * it may be an RX code, or it may be a special code (e.g. VNOVOL), or it may
127  * be a security code (e.g. RXKADEXPIRED).
128  *
129  * If the error code is from cm_Conn() or friends, connp will be NULL.
130  *
131  * For VLDB calls, fidp will be NULL.
132  *
133  * volSyncp and/or cbrp may also be NULL.
134  */
135 int
136 cm_Analyze(cm_conn_t *connp, cm_user_t *userp, cm_req_t *reqp,
137            struct cm_fid *fidp, 
138            AFSVolSync *volSyncp, 
139            cm_serverRef_t * serversp,
140            cm_callbackRequest_t *cbrp, long errorCode)
141 {
142     cm_server_t *serverp = NULL;
143     cm_serverRef_t **serverspp = NULL;
144     cm_serverRef_t *tsrp;
145     cm_cell_t  *cellp = NULL;
146     cm_ucell_t *ucellp;
147     int retry = 0;
148     int free_svr_list = 0;
149     int dead_session;
150     long timeUsed, timeLeft;
151     long code;
152     char addr[16];
153
154     osi_Log2(afsd_logp, "cm_Analyze connp 0x%p, code 0x%x",
155              connp, errorCode);
156
157     /* no locking required, since connp->serverp never changes after
158      * creation */
159     dead_session = (userp->cellInfop == NULL);
160     if (connp)
161         serverp = connp->serverp;
162
163     /* Update callback pointer */
164     if (cbrp && serverp && errorCode == 0) {
165         if (cbrp->serverp) {
166             if ( cbrp->serverp != serverp ) {
167                 lock_ObtainWrite(&cm_serverLock);
168                 cm_PutServerNoLock(cbrp->serverp);
169                 cm_GetServerNoLock(serverp);
170                 lock_ReleaseWrite(&cm_serverLock);
171             }
172         } else {
173             cm_GetServer(serverp);
174         }
175         lock_ObtainWrite(&cm_callbackLock);
176         cbrp->serverp = serverp;
177         lock_ReleaseWrite(&cm_callbackLock);
178     }
179
180     /* If not allowed to retry, don't */
181     if (reqp->flags & CM_REQ_NORETRY)
182         goto out;
183
184     /* if timeout - check that it did not exceed the SMB timeout
185      * and retry */
186     
187     /* timeleft - get if from reqp the same way as cmXonnByMServers does */
188 #ifndef DJGPP
189     timeUsed = (GetTickCount() - reqp->startTime) / 1000;
190 #else
191     gettimeofday(&now, NULL);
192     timeUsed = sub_time(now, reqp->startTime) / 1000;
193 #endif
194             
195     /* leave 5 seconds margin for sleep */
196     timeLeft = RDRtimeout - timeUsed;
197
198     if (errorCode == CM_ERROR_TIMEDOUT) {
199         if (timeLeft > 5 ) {
200             thrd_Sleep(3000);
201             if (cellp == NULL && serverp)
202                 cellp = serverp->cellp;
203             if (cellp == NULL && serversp) {
204                 struct cm_serverRef * refp;
205                 for ( refp=serversp ; cellp == NULL && refp != NULL; refp=refp->next) {
206                     if ( refp->server )
207                         cellp = refp->server->cellp;
208                 }
209             }
210             cm_CheckServers(CM_FLAG_CHECKDOWNSERVERS, cellp);
211             retry = 1;
212         }
213     } 
214
215     /* if there is nosuchvolume, then we have a situation in which a 
216      * previously known volume no longer has a set of servers 
217      * associated with it.  Either the volume has moved or
218      * the volume has been deleted.  Try to find a new server list
219      * until the timeout period expires.
220      */
221     else if (errorCode == CM_ERROR_NOSUCHVOLUME) {
222         osi_Log0(afsd_logp, "cm_Analyze passed CM_ERROR_NOSUCHVOLUME.");
223         if (timeLeft > 7) {
224             thrd_Sleep(5000);
225             
226             retry = 1;
227
228             if (fidp != NULL)   /* Not a VLDB call */
229                 cm_ForceUpdateVolume(fidp, userp, reqp);
230         }
231     }
232
233     else if (errorCode == CM_ERROR_ALLDOWN) {
234         osi_Log0(afsd_logp, "cm_Analyze passed CM_ERROR_ALLDOWN.");
235         /* Servers marked DOWN will be restored by the background daemon
236          * thread as they become available.
237          */
238     }
239
240     else if (errorCode == CM_ERROR_ALLOFFLINE) {
241         if (timeLeft > 7) {
242             osi_Log0(afsd_logp, "cm_Analyze passed CM_ERROR_ALLOFFLINE.");
243             thrd_Sleep(5000);
244             
245             if (fidp) { /* Not a VLDB call */
246                 if (!serversp) {
247                     code = cm_GetServerList(fidp, userp, reqp, &serverspp);
248                     if (code == 0) {
249                         serversp = *serverspp;
250                         free_svr_list = 1;
251                     }
252                 }
253                 if (serversp) {
254                     lock_ObtainWrite(&cm_serverLock);
255                     for (tsrp = serversp; tsrp; tsrp=tsrp->next)
256                         tsrp->status = not_busy;
257                     lock_ReleaseWrite(&cm_serverLock);
258                     if (free_svr_list) {
259                         cm_FreeServerList(&serversp);
260                         *serverspp = serversp;
261                     }
262                     retry = 1;
263                 }
264
265                 cm_ForceUpdateVolume(fidp, userp, reqp);
266             } else { /* VLDB call */
267                 if (serversp) {
268                     lock_ObtainWrite(&cm_serverLock);
269                     for (tsrp = serversp; tsrp; tsrp=tsrp->next)
270                         tsrp->status = not_busy;
271                     lock_ReleaseWrite(&cm_serverLock);
272                     if (free_svr_list) {
273                         cm_FreeServerList(&serversp);
274                         *serverspp = serversp;
275                     }
276                 }
277             }   
278         }
279     }
280
281     /* if all servers are busy, mark them non-busy and start over */
282     else if (errorCode == CM_ERROR_ALLBUSY) {
283         osi_Log0(afsd_logp, "cm_Analyze passed CM_ERROR_ALLBUSY.");
284         if (timeLeft > 7) {
285             thrd_Sleep(5000);
286             if (!serversp) {
287                 code = cm_GetServerList(fidp, userp, reqp, &serverspp);
288                 if (code == 0) {
289                     serversp = *serverspp;
290                     free_svr_list = 1;
291                 }
292             }
293             lock_ObtainWrite(&cm_serverLock);
294             for (tsrp = serversp; tsrp; tsrp=tsrp->next) {
295                 if (tsrp->status == busy)
296                     tsrp->status = not_busy;
297             }
298             lock_ReleaseWrite(&cm_serverLock);
299             if (free_svr_list) {
300                 cm_FreeServerList(&serversp);
301                 *serverspp = serversp;
302             }
303             retry = 1;
304         }
305     }
306
307     /* special codes:  VBUSY and VRESTARTING */
308     else if (errorCode == VBUSY || errorCode == VRESTARTING) {
309         if (!serversp) {
310             code = cm_GetServerList(fidp, userp, reqp, &serverspp);
311             if (code == 0) {
312                 serversp = *serverspp;
313                 free_svr_list = 1;
314             }
315         }
316         lock_ObtainWrite(&cm_serverLock);
317         for (tsrp = serversp; tsrp; tsrp=tsrp->next) {
318             if (tsrp->server == serverp
319                  && tsrp->status == not_busy) {
320                 tsrp->status = busy;
321                 break;
322             }
323         }
324         lock_ReleaseWrite(&cm_serverLock);
325         if (free_svr_list) {
326             cm_FreeServerList(&serversp);
327             *serverspp = serversp;
328         }
329         retry = 1;
330     }
331
332     /* special codes:  missing volumes */
333     else if (errorCode == VNOVOL || errorCode == VMOVED || errorCode == VOFFLINE ||
334              errorCode == VSALVAGE || errorCode == VNOSERVICE || errorCode == VIO) 
335     {       
336         char addr[16];
337         char *format;
338 #ifndef DJGPP
339         DWORD msgID;
340 #endif
341         switch ( errorCode ) {
342         case VNOVOL:
343 #ifndef DJGPP
344             msgID = MSG_SERVER_REPORTS_VNOVOL;
345 #endif
346             format = "Server %s reported volume %d as not attached.";
347             break;
348         case VMOVED:
349 #ifndef DJGPP
350             msgID = MSG_SERVER_REPORTS_VMOVED;
351 #endif
352             format = "Server %s reported volume %d as moved.";
353             break;
354         case VOFFLINE:
355 #ifndef DJGPP
356             msgID = MSG_SERVER_REPORTS_VOFFLINE;
357 #endif
358             format = "Server %s reported volume %d as offline.";
359             break;
360         case VSALVAGE:
361 #ifndef DJGPP
362             msgID = MSG_SERVER_REPORTS_VSALVAGE;
363 #endif
364             format = "Server %s reported volume %d as needs salvage.";
365             break;
366         case VNOSERVICE:
367 #ifndef DJGPP
368             msgID = MSG_SERVER_REPORTS_VNOSERVICE;
369 #endif
370             format = "Server %s reported volume %d as not in service.";
371             break;
372         case VIO:
373 #ifndef DJGPP
374             msgID = MSG_SERVER_REPORTS_VIO;
375 #endif
376             format = "Server %s reported volume %d as temporarily unaccessible.";
377             break;
378         }
379
380         /* Log server being offline for this volume */
381         sprintf(addr, "%d.%d.%d.%d", 
382                  ((serverp->addr.sin_addr.s_addr & 0xff)),
383                  ((serverp->addr.sin_addr.s_addr & 0xff00)>> 8),
384                  ((serverp->addr.sin_addr.s_addr & 0xff0000)>> 16),
385                  ((serverp->addr.sin_addr.s_addr & 0xff000000)>> 24)); 
386
387         osi_Log2(afsd_logp, format, osi_LogSaveString(afsd_logp,addr), fidp->volume);
388 #ifndef DJGPP
389         LogEvent(EVENTLOG_WARNING_TYPE, msgID, addr, fidp->volume);
390 #endif
391
392         /* Mark server offline for this volume */
393         if (!serversp) {
394             code = cm_GetServerList(fidp, userp, reqp, &serverspp);
395             if (code == 0) {
396                 serversp = *serverspp;
397                 free_svr_list = 1;
398             }
399         }
400         for (tsrp = serversp; tsrp; tsrp=tsrp->next) {
401             if (tsrp->server == serverp)
402                 tsrp->status = offline;
403         }   
404         if (free_svr_list) {
405             cm_FreeServerList(&serversp);
406             *serverspp = serversp;
407         }
408         if ( timeLeft > 2 )
409             retry = 1;
410     } else if ( errorCode == VNOVNODE ) {
411         if ( fidp ) {
412             cm_scache_t * scp;
413             osi_Log4(afsd_logp, "cm_Analyze passed VNOVNODE cell %u vol %u vn %u uniq %u.",
414                       fidp->cell, fidp->volume, fidp->vnode, fidp->unique);
415
416             scp = cm_FindSCache(fidp);
417             if (scp) {
418                 cm_scache_t *pscp = NULL;
419
420                 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
421                     pscp = cm_FindSCacheParent(scp);
422
423                 lock_ObtainWrite(&cm_scacheLock);
424                 cm_RecycleSCache(scp, CM_SCACHE_RECYCLEFLAG_DESTROY_BUFFERS);
425                 lock_ReleaseWrite(&cm_scacheLock);
426
427                 if (pscp) {
428                     if (pscp->cbExpires > 0 && pscp->cbServerp != NULL) {
429                         lock_ObtainMutex(&pscp->mx);
430                         cm_DiscardSCache(pscp);
431                         lock_ReleaseMutex(&pscp->mx);
432                     }
433                     cm_ReleaseSCache(pscp);
434                 }
435             }
436         } else {
437             osi_Log0(afsd_logp, "cm_Analyze passed VNOVNODE unknown fid.");
438         }
439     }
440
441     /* RX codes */
442     else if (errorCode == RX_CALL_TIMEOUT) {
443         /* server took longer than hardDeadTime 
444          * don't mark server as down but don't retry
445          * this is to prevent the SMB session from timing out
446          * In addition, we log an event to the event log 
447          */
448
449         /* Log server being offline for this volume */
450         sprintf(addr, "%d.%d.%d.%d", 
451                  ((serverp->addr.sin_addr.s_addr & 0xff)),
452                  ((serverp->addr.sin_addr.s_addr & 0xff00)>> 8),
453                  ((serverp->addr.sin_addr.s_addr & 0xff0000)>> 16),
454                  ((serverp->addr.sin_addr.s_addr & 0xff000000)>> 24)); 
455
456 #ifndef DJGPP
457         LogEvent(EVENTLOG_WARNING_TYPE, MSG_RX_HARD_DEAD_TIME_EXCEEDED, addr);
458 #endif /* !DJGPP */
459           
460         retry = 0;
461         osi_Log1(afsd_logp, "cm_Analyze: hardDeadTime exceeded addr[%s]",
462                  osi_LogSaveString(afsd_logp,addr));
463     }
464     else if (errorCode >= -64 && errorCode < 0) {
465         /* mark server as down */
466         lock_ObtainMutex(&serverp->mx);
467         serverp->flags |= CM_SERVERFLAG_DOWN;
468         lock_ReleaseMutex(&serverp->mx);
469         cm_ForceNewConnections(serverp);
470         if ( timeLeft > 2 )
471             retry = 1;
472     }
473     else if (errorCode == RXKADEXPIRED || 
474              errorCode == RXKADBADTICKET) {
475         if (!dead_session) {
476             lock_ObtainMutex(&userp->mx);
477             ucellp = cm_GetUCell(userp, serverp->cellp);
478             if (ucellp->ticketp) {
479                 free(ucellp->ticketp);
480                 ucellp->ticketp = NULL;
481             }
482             ucellp->flags &= ~CM_UCELLFLAG_RXKAD;
483             ucellp->gen++;
484             lock_ReleaseMutex(&userp->mx);
485             if ( timeLeft > 2 )
486                 retry = 1;
487         }
488     } else {
489         if (errorCode) {
490             char * s = "unknown error";
491             switch ( errorCode ) {
492             case RXKADINCONSISTENCY: s = "RXKADINCONSISTENCY"; break;
493             case RXKADPACKETSHORT  : s = "RXKADPACKETSHORT";   break;
494             case RXKADLEVELFAIL    : s = "RXKADLEVELFAIL";     break;
495             case RXKADTICKETLEN    : s = "RXKADTICKETLEN";     break;
496             case RXKADOUTOFSEQUENCE: s = "RXKADOUTOFSEQUENCE"; break;
497             case RXKADNOAUTH       : s = "RXKADNOAUTH";        break;
498             case RXKADBADKEY       : s = "RXKADBADKEY";        break;
499             case RXKADBADTICKET    : s = "RXKADBADTICKET";     break;
500             case RXKADUNKNOWNKEY   : s = "RXKADUNKNOWNKEY";    break;
501             case RXKADEXPIRED      : s = "RXKADEXPIRED";       break;
502             case RXKADSEALEDINCON  : s = "RXKADSEALEDINCON";   break;
503             case RXKADDATALEN      : s = "RXKADDATALEN";       break;
504             case RXKADILLEGALLEVEL : s = "RXKADILLEGALLEVEL";  break;
505             case VSALVAGE          : s = "VSALVAGE";           break;
506             case VNOVNODE          : s = "VNOVNODE";           break;
507             case VNOVOL            : s = "VNOVOL";             break;
508             case VVOLEXISTS        : s = "VVOLEXISTS";         break;
509             case VNOSERVICE        : s = "VNOSERVICE";         break;
510             case VOFFLINE          : s = "VOFFLINE";           break;
511             case VONLINE           : s = "VONLINE";            break;
512             case VDISKFULL         : s = "VDISKFULL";          break;
513             case VOVERQUOTA        : s = "VOVERQUOTA";         break;
514             case VBUSY             : s = "VBUSY";              break;
515             case VMOVED            : s = "VMOVED";             break;
516             case VIO               : s = "VIO";                break;
517             case VRESTRICTED       : s = "VRESTRICTED";        break;
518             case VRESTARTING       : s = "VRESTARTING";        break;
519             case VREADONLY         : s = "VREADONLY";          break;
520             case EAGAIN            : s = "EAGAIN";             break;
521             case UAEAGAIN          : s = "UAEAGAIN";           break;
522             case EINVAL            : s = "EINVAL";             break;
523             case UAEINVAL          : s = "UAEINVAL";           break;
524             case EACCES            : s = "EACCES";             break;
525             case UAEACCES          : s = "UAECCES";            break;
526             case ENOENT            : s = "ENOENT";             break;
527             case UAENOENT          : s = "UAENOENT";           break;
528             case CM_ERROR_NOSUCHCELL        : s = "CM_ERROR_NOSUCHCELL";         break;                         
529             case CM_ERROR_NOSUCHVOLUME      : s = "CM_ERROR_NOSUCHVOLUME";       break;                         
530             case CM_ERROR_TIMEDOUT          : s = "CM_ERROR_TIMEDOUT";           break;                 
531             case CM_ERROR_RETRY             : s = "CM_ERROR_RETRY";              break; 
532             case CM_ERROR_NOACCESS          : s = "CM_ERROR_NOACCESS";           break; 
533             case CM_ERROR_NOSUCHFILE        : s = "CM_ERROR_NOSUCHFILE";         break;                         
534             case CM_ERROR_STOPNOW           : s = "CM_ERROR_STOPNOW";            break;                         
535             case CM_ERROR_TOOBIG            : s = "CM_ERROR_TOOBIG";             break;                                 
536             case CM_ERROR_INVAL             : s = "CM_ERROR_INVAL";              break;                                 
537             case CM_ERROR_BADFD             : s = "CM_ERROR_BADFD";              break;                                 
538             case CM_ERROR_BADFDOP           : s = "CM_ERROR_BADFDOP";            break;                         
539             case CM_ERROR_EXISTS            : s = "CM_ERROR_EXISTS";             break;                                 
540             case CM_ERROR_CROSSDEVLINK      : s = "CM_ERROR_CROSSDEVLINK";       break;                         
541             case CM_ERROR_BADOP             : s = "CM_ERROR_BADOP";              break;                                 
542             case CM_ERROR_BADPASSWORD       : s = "CM_ERROR_BADPASSWORD";        break;         
543             case CM_ERROR_NOTDIR            : s = "CM_ERROR_NOTDIR";             break;                                 
544             case CM_ERROR_ISDIR             : s = "CM_ERROR_ISDIR";              break;                                 
545             case CM_ERROR_READONLY          : s = "CM_ERROR_READONLY";           break;                         
546             case CM_ERROR_WOULDBLOCK        : s = "CM_ERROR_WOULDBLOCK";         break;                         
547             case CM_ERROR_QUOTA             : s = "CM_ERROR_QUOTA";              break;                                 
548             case CM_ERROR_SPACE             : s = "CM_ERROR_SPACE";              break;                                 
549             case CM_ERROR_BADSHARENAME      : s = "CM_ERROR_BADSHARENAME";       break;                         
550             case CM_ERROR_BADTID            : s = "CM_ERROR_BADTID";             break;                                 
551             case CM_ERROR_UNKNOWN           : s = "CM_ERROR_UNKNOWN";            break;                         
552             case CM_ERROR_NOMORETOKENS      : s = "CM_ERROR_NOMORETOKENS";       break;                         
553             case CM_ERROR_NOTEMPTY          : s = "CM_ERROR_NOTEMPTY";           break;                         
554             case CM_ERROR_USESTD            : s = "CM_ERROR_USESTD";             break;                                 
555             case CM_ERROR_REMOTECONN        : s = "CM_ERROR_REMOTECONN";         break;                         
556             case CM_ERROR_ATSYS             : s = "CM_ERROR_ATSYS";              break;                                 
557             case CM_ERROR_NOSUCHPATH        : s = "CM_ERROR_NOSUCHPATH";         break;                         
558             case CM_ERROR_CLOCKSKEW         : s = "CM_ERROR_CLOCKSKEW";          break;                         
559             case CM_ERROR_BADSMB            : s = "CM_ERROR_BADSMB";             break;                                 
560             case CM_ERROR_ALLBUSY           : s = "CM_ERROR_ALLBUSY";            break;                         
561             case CM_ERROR_NOFILES           : s = "CM_ERROR_NOFILES";            break;                         
562             case CM_ERROR_PARTIALWRITE      : s = "CM_ERROR_PARTIALWRITE";       break;                         
563             case CM_ERROR_NOIPC             : s = "CM_ERROR_NOIPC";              break;                                 
564             case CM_ERROR_BADNTFILENAME     : s = "CM_ERROR_BADNTFILENAME";      break;                         
565             case CM_ERROR_BUFFERTOOSMALL    : s = "CM_ERROR_BUFFERTOOSMALL";     break;                         
566             case CM_ERROR_RENAME_IDENTICAL  : s = "CM_ERROR_RENAME_IDENTICAL";   break;                 
567             case CM_ERROR_ALLOFFLINE        : s = "CM_ERROR_ALLOFFLINE";         break;          
568             case CM_ERROR_AMBIGUOUS_FILENAME: s = "CM_ERROR_AMBIGUOUS_FILENAME"; break;  
569             case CM_ERROR_BADLOGONTYPE      : s = "CM_ERROR_BADLOGONTYPE";       break;             
570             case CM_ERROR_GSSCONTINUE       : s = "CM_ERROR_GSSCONTINUE";        break;         
571             case CM_ERROR_TIDIPC            : s = "CM_ERROR_TIDIPC";             break;              
572             case CM_ERROR_TOO_MANY_SYMLINKS : s = "CM_ERROR_TOO_MANY_SYMLINKS";  break;   
573             case CM_ERROR_PATH_NOT_COVERED  : s = "CM_ERROR_PATH_NOT_COVERED";   break;    
574             case CM_ERROR_LOCK_CONFLICT     : s = "CM_ERROR_LOCK_CONFLICT";      break;       
575             case CM_ERROR_SHARING_VIOLATION : s = "CM_ERROR_SHARING_VIOLATION";  break;   
576             case CM_ERROR_ALLDOWN           : s = "CM_ERROR_ALLDOWN";            break;             
577             case CM_ERROR_TOOFEWBUFS        : s = "CM_ERROR_TOOFEWBUFS";         break;                         
578             case CM_ERROR_TOOMANYBUFS       : s = "CM_ERROR_TOOMANYBUFS";        break;                         
579             }
580             osi_Log2(afsd_logp, "cm_Analyze: ignoring error code 0x%x (%s)", 
581                      errorCode, s);
582             retry = 0;
583         }
584     }
585
586     if (retry && dead_session)
587         retry = 0;
588
589   out:
590     /* drop this on the way out */
591     if (connp)
592         cm_PutConn(connp);
593
594     /* retry until we fail to find a connection */
595     return retry;
596 }
597
598 long cm_ConnByMServers(cm_serverRef_t *serversp, cm_user_t *usersp,
599         cm_req_t *reqp, cm_conn_t **connpp)
600 {
601     long code;
602     cm_serverRef_t *tsrp;
603     cm_server_t *tsp;
604     long firstError = 0;
605     int someBusy = 0, someOffline = 0, allOffline = 1, allBusy = 1, allDown = 1;
606     long timeUsed, timeLeft, hardTimeLeft;
607 #ifdef DJGPP
608     struct timeval now;
609 #endif /* DJGPP */        
610
611     if (serversp == NULL) {
612         osi_Log1(afsd_logp, "cm_ConnByMServers returning 0x%x", CM_ERROR_NOSUCHVOLUME);
613         return CM_ERROR_NOSUCHVOLUME;
614     }
615
616     *connpp = NULL;
617
618 #ifndef DJGPP
619     timeUsed = (GetTickCount() - reqp->startTime) / 1000;
620 #else
621     gettimeofday(&now, NULL);
622     timeUsed = sub_time(now, reqp->startTime) / 1000;
623 #endif
624         
625     /* leave 5 seconds margin of safety */
626     timeLeft =  ConnDeadtimeout - timeUsed - 5;
627     hardTimeLeft = HardDeadtimeout - timeUsed - 5;
628
629     lock_ObtainWrite(&cm_serverLock);
630     for (tsrp = serversp; tsrp; tsrp=tsrp->next) {
631         tsp = tsrp->server;
632         cm_GetServerNoLock(tsp);
633         lock_ReleaseWrite(&cm_serverLock);
634         if (!(tsp->flags & CM_SERVERFLAG_DOWN)) {
635             allDown = 0;
636             if (tsrp->status == busy) {
637                 allOffline = 0;
638                 someBusy = 1;
639             } else if (tsrp->status == offline) {
640                 allBusy = 0;
641                 someOffline = 1;
642             } else {
643                 allOffline = 0;
644                 allBusy = 0;
645                 code = cm_ConnByServer(tsp, usersp, connpp);
646                 if (code == 0) {        /* cm_CBS only returns 0 */
647                     cm_PutServer(tsp);
648                     /* Set RPC timeout */
649                     if (timeLeft > ConnDeadtimeout)
650                         timeLeft = ConnDeadtimeout;
651
652                     if (hardTimeLeft > HardDeadtimeout) 
653                         hardTimeLeft = HardDeadtimeout;
654
655                     lock_ObtainMutex(&(*connpp)->mx);
656                     rx_SetConnDeadTime((*connpp)->callp, timeLeft);
657                     rx_SetConnHardDeadTime((*connpp)->callp, (u_short) hardTimeLeft);
658                     lock_ReleaseMutex(&(*connpp)->mx);
659                     return 0;
660                 }
661                 
662                 /* therefore, this code is never executed */
663                 if (firstError == 0)
664                     firstError = code;
665             }
666         }
667         lock_ObtainWrite(&cm_serverLock);
668         cm_PutServerNoLock(tsp);
669     }   
670     lock_ReleaseWrite(&cm_serverLock);
671
672     if (firstError == 0) {
673         if (allDown) 
674             firstError = CM_ERROR_ALLDOWN;
675         else if (allBusy) 
676             firstError = CM_ERROR_ALLBUSY;
677         else if (allOffline || (someBusy && someOffline))
678             firstError = CM_ERROR_ALLOFFLINE;
679         else {
680             osi_Log0(afsd_logp, "cm_ConnByMServers returning impossible error TIMEDOUT");
681             firstError = CM_ERROR_TIMEDOUT;
682         }
683     }
684
685     osi_Log1(afsd_logp, "cm_ConnByMServers returning 0x%x", firstError);
686     return firstError;
687 }
688
689 /* called with a held server to GC all bad connections hanging off of the server */
690 void cm_GCConnections(cm_server_t *serverp)
691 {
692     cm_conn_t *tcp;
693     cm_conn_t **lcpp;
694     cm_user_t *userp;
695
696     lock_ObtainWrite(&cm_connLock);
697     lcpp = &serverp->connsp;
698     for (tcp = *lcpp; tcp; tcp = *lcpp) {
699         userp = tcp->userp;
700         if (userp && tcp->refCount == 0 && (userp->vcRefs == 0)) {
701             /* do the deletion of this guy */
702             cm_PutServer(tcp->serverp);
703             cm_ReleaseUser(userp);
704             *lcpp = tcp->nextp;
705             rx_DestroyConnection(tcp->callp);
706             lock_FinalizeMutex(&tcp->mx);
707             free(tcp);
708         }
709         else {
710             /* just advance to the next */
711             lcpp = &tcp->nextp;
712         }
713     }
714     lock_ReleaseWrite(&cm_connLock);
715 }
716
717 static void cm_NewRXConnection(cm_conn_t *tcp, cm_ucell_t *ucellp,
718                                cm_server_t *serverp)
719 {
720     unsigned short port;
721     int serviceID;
722     int secIndex;
723     struct rx_securityClass *secObjp;
724
725     if (serverp->type == CM_SERVER_VLDB) {
726         port = htons(7003);
727         serviceID = 52;
728     }
729     else {
730         osi_assert(serverp->type == CM_SERVER_FILE);
731         port = htons(7000);
732         serviceID = 1;
733     }
734     if (ucellp->flags & CM_UCELLFLAG_RXKAD) {
735         secIndex = 2;
736         if (cryptall) {
737             tcp->cryptlevel = rxkad_crypt;
738         } else {
739             tcp->cryptlevel = rxkad_clear;
740         }
741         secObjp = rxkad_NewClientSecurityObject(tcp->cryptlevel,
742                                                 &ucellp->sessionKey, ucellp->kvno,
743                                                 ucellp->ticketLen, ucellp->ticketp);    
744     } else {
745         /* normal auth */
746         secIndex = 0;
747         tcp->cryptlevel = rxkad_clear;
748         secObjp = rxnull_NewClientSecurityObject();
749     }
750     osi_assert(secObjp != NULL);
751     tcp->callp = rx_NewConnection(serverp->addr.sin_addr.s_addr,
752                                   port,
753                                   serviceID,
754                                   secObjp,
755                                   secIndex);
756     rx_SetConnDeadTime(tcp->callp, ConnDeadtimeout);
757     rx_SetConnHardDeadTime(tcp->callp, HardDeadtimeout);
758     tcp->ucgen = ucellp->gen;
759     if (secObjp)
760         rxs_Release(secObjp);   /* Decrement the initial refCount */
761 }
762
763 long cm_ConnByServer(cm_server_t *serverp, cm_user_t *userp, cm_conn_t **connpp)
764 {
765     cm_conn_t *tcp;
766     cm_ucell_t *ucellp;
767
768     lock_ObtainMutex(&userp->mx);
769     lock_ObtainWrite(&cm_connLock);
770     for (tcp = serverp->connsp; tcp; tcp=tcp->nextp) {
771         if (tcp->userp == userp) 
772             break;
773     }
774     
775     /* find ucell structure */
776     ucellp = cm_GetUCell(userp, serverp->cellp);
777     if (!tcp) {
778         cm_GetServer(serverp);
779         tcp = malloc(sizeof(*tcp));
780         memset(tcp, 0, sizeof(*tcp));
781         tcp->nextp = serverp->connsp;
782         serverp->connsp = tcp;
783         cm_HoldUser(userp);
784         tcp->userp = userp;
785         lock_InitializeMutex(&tcp->mx, "cm_conn_t mutex");
786         lock_ObtainMutex(&tcp->mx);
787         tcp->serverp = serverp;
788         tcp->cryptlevel = rxkad_clear;
789         cm_NewRXConnection(tcp, ucellp, serverp);
790         tcp->refCount = 1;
791         lock_ReleaseMutex(&tcp->mx);
792     } else {
793         if ((tcp->flags & CM_CONN_FLAG_FORCE_NEW) ||
794             (tcp->ucgen < ucellp->gen) ||
795             (tcp->cryptlevel != (cryptall ? (ucellp->flags & CM_UCELLFLAG_RXKAD ? rxkad_crypt : rxkad_clear) : rxkad_clear)))
796         {
797             if (tcp->ucgen < ucellp->gen)
798                 osi_Log0(afsd_logp, "cm_ConnByServer replace connection due to token update");
799             else
800                 osi_Log0(afsd_logp, "cm_ConnByServer replace connection due to crypt change");
801             lock_ObtainMutex(&tcp->mx);
802             tcp->flags &= ~CM_CONN_FLAG_FORCE_NEW;
803             rx_DestroyConnection(tcp->callp);
804             cm_NewRXConnection(tcp, ucellp, serverp);
805             lock_ReleaseMutex(&tcp->mx);
806         }
807         tcp->refCount++;
808     }
809     lock_ReleaseWrite(&cm_connLock);
810     lock_ReleaseMutex(&userp->mx);
811
812     /* return this pointer to our caller */
813     osi_Log1(afsd_logp, "cm_ConnByServer returning conn 0x%p", tcp);
814     *connpp = tcp;
815
816     return 0;
817 }
818
819 long cm_Conn(struct cm_fid *fidp, struct cm_user *userp, cm_req_t *reqp,
820              cm_conn_t **connpp)
821 {
822     long code;
823
824     cm_serverRef_t **serverspp;
825
826     code = cm_GetServerList(fidp, userp, reqp, &serverspp);
827     if (code) {
828         *connpp = NULL;
829         return code;
830     }
831
832     code = cm_ConnByMServers(*serverspp, userp, reqp, connpp);
833     cm_FreeServerList(serverspp);
834     return code;
835 }
836
837 extern struct rx_connection * 
838 cm_GetRxConn(cm_conn_t *connp)
839 {
840     struct rx_connection * rxconn;
841     lock_ObtainMutex(&connp->mx);
842     rxconn = connp->callp;
843     rx_GetConnection(rxconn);
844     lock_ReleaseMutex(&connp->mx);
845     return rxconn;
846 }
847
848 void cm_ForceNewConnections(cm_server_t *serverp)
849 {
850     cm_conn_t *tcp;
851
852     lock_ObtainWrite(&cm_connLock);
853     for (tcp = serverp->connsp; tcp; tcp=tcp->nextp) {
854         lock_ObtainMutex(&tcp->mx);
855         tcp->flags |= CM_CONN_FLAG_FORCE_NEW;
856         lock_ReleaseMutex(&tcp->mx);
857     }
858     lock_ReleaseWrite(&cm_connLock);
859 }