Windows: cm_TryBulkStatRPC must process VIO errors
[openafs.git] / src / WINNT / afsd / cm_vnodeops.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 #include <windows.h>
14 #include <winsock2.h>
15 #include <stddef.h>
16 #include <malloc.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <errno.h>
20
21 #include <osi.h>
22
23 #include "afsd.h"
24 #include "smb.h"
25 #include "cm_btree.h"
26
27 #include <strsafe.h>
28
29 #ifdef DEBUG
30 extern void afsi_log(char *pattern, ...);
31 #endif
32
33 int cm_enableServerLocks = 1;
34
35 int cm_followBackupPath = 0;
36
37 /*
38  * Case-folding array.  This was constructed by inspecting of SMBtrace output.
39  * I do not know anything more about it.
40  */
41 unsigned char cm_foldUpper[256] = {
42      0x0,  0x1,  0x2,  0x3,  0x4,  0x5,  0x6,  0x7,
43      0x8,  0x9,  0xa,  0xb,  0xc,  0xd,  0xe,  0xf,
44     0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
45     0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
46     0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
47     0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
48     0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
49     0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
50     0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
51     0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
52     0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
53     0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
54     0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
55     0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
56     0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
57     0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
58     0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
59     0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
60     0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
61     0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
62     0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
63     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
64     0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
65     0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
66     0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
67     0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
68     0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
69     0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
70     0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
71     0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
72     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
73     0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
74 };
75
76 /*
77  * Case-insensitive string comparison.  We used to use stricmp, but it doesn't
78  * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
79  * upper-case u-umlaut).
80  */
81 int cm_stricmp(const char *str1, const char *str2)
82 {
83     char c1, c2;
84
85     while (1) {
86         if (*str1 == 0)
87             if (*str2 == 0)
88                 return 0;
89             else
90                 return -1;
91         if (*str2 == 0)
92             return 1;
93         c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
94         c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
95         if (c1 < c2)
96             return -1;
97         if (c1 > c2)
98             return 1;
99     }
100 }
101
102
103
104 /* return success if we can open this file in this mode */
105 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
106                   cm_req_t *reqp)
107 {
108     long rights;
109     long code;
110
111     rights = 0;
112     if (openMode != 1) 
113         rights |= PRSFS_READ;
114     if (openMode == 1 || openMode == 2 || trunc) 
115         rights |= PRSFS_WRITE;
116         
117     lock_ObtainWrite(&scp->rw);
118
119     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
120                       CM_SCACHESYNC_GETSTATUS
121                      | CM_SCACHESYNC_NEEDCALLBACK
122                      | CM_SCACHESYNC_LOCK);
123
124     if (code == 0 && 
125         ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
126         scp->fileType == CM_SCACHETYPE_FILE) {
127
128         cm_key_t key;
129         unsigned int sLockType;
130         LARGE_INTEGER LOffset, LLength;
131
132         /* Check if there's some sort of lock on the file at the
133            moment. */
134
135         key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
136
137         if (rights & PRSFS_WRITE)
138             sLockType = 0;
139         else
140             sLockType = LOCKING_ANDX_SHARED_LOCK;
141
142         LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
143         LOffset.LowPart  = CM_FLSHARE_OFFSET_LOW;
144         LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
145         LLength.LowPart  = CM_FLSHARE_LENGTH_LOW;
146
147         code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
148
149         if (code == 0) {
150             cm_Unlock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp);
151         } else {
152             /* In this case, we allow the file open to go through even
153                though we can't enforce mandatory locking on the
154                file. */
155             if (code == CM_ERROR_NOACCESS &&
156                 !(rights & PRSFS_WRITE))
157                 code = 0;
158             else {
159                 switch (code) {
160                 case CM_ERROR_ALLOFFLINE:
161                 case CM_ERROR_ALLDOWN:
162                 case CM_ERROR_ALLBUSY:
163                 case CM_ERROR_TIMEDOUT:
164                 case CM_ERROR_RETRY:
165                 case CM_ERROR_WOULDBLOCK:
166                     break;
167                 default:
168                     code = CM_ERROR_SHARING_VIOLATION;
169                 }
170             }
171         }
172
173     } else if (code != 0) {
174         goto _done;
175     }
176
177     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
178
179  _done:
180
181     lock_ReleaseWrite(&scp->rw);
182
183     return code;
184 }
185
186 /* return success if we can open this file in this mode */
187 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
188                     unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp, 
189                     cm_lock_data_t **ldpp)
190 {
191     long rights;
192     long code;
193
194     osi_assertx(ldpp != NULL, "null cm_lock_data_t");
195     *ldpp = NULL;
196
197     /* Always allow delete; the RPC will tell us if it's OK */
198     if (desiredAccess == DELETE)
199         return 0;
200
201     rights = 0;
202
203     if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
204         rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
205
206     /* We used to require PRSFS_WRITE if createDisp was 4
207        (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
208        However, we don't need to do that since the existence of the
209        scp implies that we don't need to create it. */
210     if (desiredAccess & AFS_ACCESS_WRITE)
211         rights |= PRSFS_WRITE;
212
213     lock_ObtainWrite(&scp->rw);
214
215     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
216                       CM_SCACHESYNC_GETSTATUS
217                      | CM_SCACHESYNC_NEEDCALLBACK
218                      | CM_SCACHESYNC_LOCK);
219
220     /*
221      * If the open will fail because the volume is readonly, then we will
222      * return an access denied error instead.  This is to help brain-dead
223      * apps run correctly on replicated volumes.
224      * See defect 10007 for more information.
225      */
226     if (code == CM_ERROR_READONLY)
227         code = CM_ERROR_NOACCESS;
228
229     if (code == 0 &&
230              ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
231              scp->fileType == CM_SCACHETYPE_FILE) {
232         cm_key_t key;
233         unsigned int sLockType;
234         LARGE_INTEGER LOffset, LLength;
235
236         /* Check if there's some sort of lock on the file at the
237            moment. */
238
239         key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
240         if (rights & PRSFS_WRITE)
241             sLockType = 0;
242         else
243             sLockType = LOCKING_ANDX_SHARED_LOCK;
244
245         /* single byte lock at offset 0x0100 0000 0000 0000 */
246         LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
247         LOffset.LowPart  = CM_FLSHARE_OFFSET_LOW;
248         LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
249         LLength.LowPart  = CM_FLSHARE_LENGTH_LOW;
250
251         code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
252
253         if (code == 0) {
254             (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
255             if (!*ldpp) {
256                 code = ENOMEM;
257                 goto _done;
258             }
259
260             (*ldpp)->key = key;
261             (*ldpp)->sLockType = sLockType;
262             (*ldpp)->LOffset.HighPart = LOffset.HighPart;
263             (*ldpp)->LOffset.LowPart = LOffset.LowPart;
264             (*ldpp)->LLength.HighPart = LLength.HighPart;
265             (*ldpp)->LLength.LowPart = LLength.LowPart;
266         } else {
267             /* In this case, we allow the file open to go through even
268                though we can't enforce mandatory locking on the
269                file. */
270             if (code == CM_ERROR_NOACCESS &&
271                 !(rights & PRSFS_WRITE))
272                 code = 0;
273             else {
274                 switch (code) {
275                 case CM_ERROR_ALLOFFLINE:
276                 case CM_ERROR_ALLDOWN:
277                 case CM_ERROR_ALLBUSY:
278                 case CM_ERROR_TIMEDOUT:
279                 case CM_ERROR_RETRY:
280                 case CM_ERROR_WOULDBLOCK:
281                     break;
282                 default:
283                     code = CM_ERROR_SHARING_VIOLATION;
284                 }
285             }
286         }
287     } else if (code != 0) {
288         goto _done;
289     }
290
291  _done:
292     lock_ReleaseWrite(&scp->rw);
293
294     osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
295     return code;
296 }
297
298 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, 
299                                cm_lock_data_t ** ldpp)
300 {
301     osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
302     lock_ObtainWrite(&scp->rw);
303     if (*ldpp) {
304         cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength, 
305                   (*ldpp)->key, 0, userp, reqp);
306         free(*ldpp);
307         *ldpp = NULL;
308     }
309     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
310     lock_ReleaseWrite(&scp->rw);
311     return 0;
312 }
313 /*
314  * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
315  * done in three steps:
316  * (1) open for deletion (NT_CREATE_AND_X)
317  * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
318  * (3) close (CLOSE)
319  * We must not do the RPC until step 3.  But if we are going to return an error
320  * code (e.g. directory not empty), we must return it by step 2, otherwise most
321  * clients will not notice it.  So we do a preliminary check.  For deleting
322  * files, this is almost free, since we have already done the RPC to get the
323  * parent directory's status bits.  But for deleting directories, we must do an
324  * additional RPC to get the directory's data to check if it is empty.  Sigh.
325  */
326 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
327         cm_req_t *reqp)
328 {
329     long code;
330     osi_hyper_t thyper;
331     cm_buf_t *bufferp;
332     cm_dirEntry_t *dep = 0;
333     unsigned short *hashTable;
334     unsigned int i, idx;
335     int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
336     int releaseLock = 0;
337
338     /* First check permissions */
339     lock_ObtainWrite(&scp->rw);
340     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
341                       CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
342     if (!code)
343         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
344     lock_ReleaseWrite(&scp->rw);
345     if (code)
346         return code;
347
348     /* If deleting directory, must be empty */
349
350     if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
351         return code;
352
353     thyper.HighPart = 0; thyper.LowPart = 0;
354     code = buf_Get(scp, &thyper, reqp, &bufferp);
355     if (code)
356         return code;
357
358     lock_ObtainMutex(&bufferp->mx);
359     lock_ObtainWrite(&scp->rw);
360     releaseLock = 1;
361     while (1) {
362         code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
363                           CM_SCACHESYNC_NEEDCALLBACK
364                           | CM_SCACHESYNC_READ
365                           | CM_SCACHESYNC_BUFLOCKED);
366         if (code)
367             goto done;
368
369         if (cm_HaveBuffer(scp, bufferp, 1))
370             break;
371
372         /* otherwise, load the buffer and try again */
373         lock_ReleaseMutex(&bufferp->mx);
374         code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
375         lock_ReleaseWrite(&scp->rw);
376         lock_ObtainMutex(&bufferp->mx);
377         lock_ObtainWrite(&scp->rw);
378         cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
379         if (code)
380             goto done;
381     }
382
383     lock_ReleaseWrite(&scp->rw);
384     releaseLock = 0;
385
386     /* We try to determine emptiness without looking beyond the first page,
387      * and without assuming "." and ".." are present and are on the first
388      * page (though these assumptions might, after all, be reasonable).
389      */
390     hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
391     for (i=0; i<128; i++) {
392         idx = ntohs(hashTable[i]);
393         while (idx) {
394             if (idx >= 64) {
395                 BeyondPage = 1;
396                 break;
397             }
398             dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
399             if (strcmp(dep->name, ".") == 0)
400                 HaveDot = 1;
401             else if (strcmp(dep->name, "..") == 0)
402                 HaveDotDot = 1;
403             else {
404                 code = CM_ERROR_NOTEMPTY;
405                 goto done;
406             }
407             idx = ntohs(dep->next);
408         }
409     }
410     if (BeyondPage && HaveDot && HaveDotDot)
411         code = CM_ERROR_NOTEMPTY;
412     else
413         code = 0;
414   done:   
415     lock_ReleaseMutex(&bufferp->mx);
416     buf_Release(bufferp);
417     if (releaseLock)
418         lock_ReleaseWrite(&scp->rw);
419     return code;
420 }       
421
422 /*
423  * Iterate through all entries in a directory.
424  * When the function funcp is called, the buffer is locked but the
425  * directory vnode is not.
426  *
427  * If the retscp parameter is not NULL, the parmp must be a 
428  * cm_lookupSearch_t object.  
429  */
430 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
431                  osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
432                  cm_scache_t **retscp)
433 {
434     char *tp;
435     long code;
436     cm_dirEntry_t *dep = 0;
437     cm_buf_t *bufferp;
438     long temp;
439     osi_hyper_t dirLength;
440     osi_hyper_t bufferOffset;
441     osi_hyper_t curOffset;
442     osi_hyper_t thyper;
443     long entryInDir;
444     long entryInBuffer;
445     cm_pageHeader_t *pageHeaderp;
446     int slotInPage;
447     long nextEntryCookie;
448     int numDirChunks;   /* # of 32 byte dir chunks in this entry */
449         
450     /* get the directory size */
451     lock_ObtainWrite(&scp->rw);
452     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
453                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
454     lock_ReleaseWrite(&scp->rw);
455     if (code)
456         return code;
457         
458     if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
459         return CM_ERROR_NOTDIR;
460
461     if (retscp)                         /* if this is a lookup call */
462     {
463         cm_lookupSearch_t*      sp = parmp;
464
465         if (
466 #ifdef AFS_FREELANCE_CLIENT
467         /* Freelance entries never end up in the DNLC because they
468          * do not have an associated cm_server_t
469          */
470             !(cm_freelanceEnabled &&
471             sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
472               sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
473 #else /* !AFS_FREELANCE_CLIENT */
474             TRUE
475 #endif
476             ) 
477         {
478             int casefold = sp->caseFold;
479             sp->caseFold = 0; /* we have a strong preference for exact matches */
480             if ( *retscp = cm_dnlcLookup(scp, sp))      /* dnlc hit */
481             {
482                 sp->caseFold = casefold;
483                 return 0;
484             }
485             sp->caseFold = casefold;
486         }
487
488         /*
489          * see if we can find it using the directory hash tables.
490          * we can only do exact matches, since the hash is case
491          * sensitive.
492          */
493         if (funcp != (cm_DirFuncp_t)cm_BPlusDirFoo)
494         {
495             cm_dirOp_t dirop;
496 #ifdef USE_BPLUS
497             int usedBplus = 0;
498 #endif
499
500             code = ENOENT;
501
502             code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ,
503                                  CM_DIROP_FLAG_NONE, &dirop);
504             if (code == 0) {
505
506 #ifdef USE_BPLUS
507                 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
508                 if (code != EINVAL)
509                     usedBplus = 1;
510                 else
511 #endif
512                     code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
513
514                 cm_EndDirOp(&dirop);
515             }
516
517             if (code == 0) {
518                 /* found it */
519                 sp->found = TRUE;
520                 sp->ExactFound = TRUE;
521                 *retscp = NULL; /* force caller to call cm_GetSCache() */
522                 return 0;
523             }
524 #ifdef USE_BPLUS
525             if (usedBplus) {
526                 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
527                     /* found it */
528                     sp->found = TRUE;
529                     sp->ExactFound = FALSE;
530                     *retscp = NULL; /* force caller to call cm_GetSCache() */
531                     return 0;
532                 }
533
534                 return CM_ERROR_BPLUS_NOMATCH;
535             }
536 #endif
537         }
538     }   
539
540     /*
541      * XXX We only get the length once.  It might change when we drop the
542      * lock.
543      */
544     dirLength = scp->length;
545
546     bufferp = NULL;
547     bufferOffset.LowPart = bufferOffset.HighPart = 0;
548     if (startOffsetp)
549         curOffset = *startOffsetp;
550     else {
551         curOffset.HighPart = 0;
552         curOffset.LowPart = 0;
553     }   
554
555     while (1) {
556         /* make sure that curOffset.LowPart doesn't point to the first
557          * 32 bytes in the 2nd through last dir page, and that it
558          * doesn't point at the first 13 32-byte chunks in the first
559          * dir page, since those are dir and page headers, and don't
560          * contain useful information.
561          */
562         temp = curOffset.LowPart & (2048-1);
563         if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
564             /* we're in the first page */
565             if (temp < 13*32) temp = 13*32;
566         }
567         else {
568             /* we're in a later dir page */
569             if (temp < 32) temp = 32;
570         }       
571                 
572         /* make sure the low order 5 bits are zero */
573         temp &= ~(32-1);
574                 
575         /* now put temp bits back ito curOffset.LowPart */
576         curOffset.LowPart &= ~(2048-1);
577         curOffset.LowPart |= temp;
578
579         /* check if we've passed the dir's EOF */
580         if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
581             break;
582                 
583         /* see if we can use the bufferp we have now; compute in which
584          * page the current offset would be, and check whether that's
585          * the offset of the buffer we have.  If not, get the buffer.
586          */
587         thyper.HighPart = curOffset.HighPart;
588         thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
589         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
590             /* wrong buffer */
591             if (bufferp) {
592                 lock_ReleaseMutex(&bufferp->mx);
593                 buf_Release(bufferp);
594                 bufferp = NULL;
595             }
596
597             code = buf_Get(scp, &thyper, reqp, &bufferp);
598             if (code) {
599                 /* if buf_Get() fails we do not have a buffer object to lock */
600                 bufferp = NULL;
601                 break;
602             }
603
604             lock_ObtainMutex(&bufferp->mx);
605             bufferOffset = thyper;
606
607             /* now get the data in the cache */
608             while (1) {
609                 lock_ObtainWrite(&scp->rw);
610                 code = cm_SyncOp(scp, bufferp, userp, reqp,
611                                   PRSFS_LOOKUP,
612                                   CM_SCACHESYNC_NEEDCALLBACK
613                                   | CM_SCACHESYNC_READ
614                                   | CM_SCACHESYNC_BUFLOCKED);
615                 if (code) {
616                     lock_ReleaseWrite(&scp->rw);
617                     break;
618                 }
619                 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
620                                 
621                 if (cm_HaveBuffer(scp, bufferp, 1)) {
622                     lock_ReleaseWrite(&scp->rw);
623                     break;
624                 }
625
626                 /* otherwise, load the buffer and try again */
627                 lock_ReleaseMutex(&bufferp->mx);
628                 code = cm_GetBuffer(scp, bufferp, NULL, userp,
629                                     reqp);
630                 lock_ReleaseWrite(&scp->rw);
631                 lock_ObtainMutex(&bufferp->mx);
632                 if (code) 
633                     break;
634             }
635             if (code) {
636                 lock_ReleaseMutex(&bufferp->mx);
637                 buf_Release(bufferp);
638                 bufferp = NULL;
639                 break;
640             }
641         }       /* if (wrong buffer) ... */
642            
643         /* now we have the buffer containing the entry we're interested
644          * in; copy it out if it represents a non-deleted entry.
645          */
646         entryInDir = curOffset.LowPart & (2048-1);
647         entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
648
649         /* page header will help tell us which entries are free.  Page
650          * header can change more often than once per buffer, since
651          * AFS 3 dir page size may be less than (but not more than) a
652          * buffer package buffer.
653          */
654         /* only look intra-buffer */
655         temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
656         temp &= ~(2048 - 1);    /* turn off intra-page bits */
657         pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
658
659         /* now determine which entry we're looking at in the page.  If
660          * it is free (there's a free bitmap at the start of the dir),
661          * we should skip these 32 bytes.
662          */
663         slotInPage = (entryInDir & 0x7e0) >> 5;
664         if (!(pageHeaderp->freeBitmap[slotInPage>>3]
665                & (1 << (slotInPage & 0x7)))) {
666             /* this entry is free */
667             numDirChunks = 1;   /* only skip this guy */
668             goto nextEntry;
669         }
670
671         tp = bufferp->datap + entryInBuffer;
672         dep = (cm_dirEntry_t *) tp;     /* now points to AFS3 dir entry */
673
674         /* while we're here, compute the next entry's location, too,
675          * since we'll need it when writing out the cookie into the
676          * dir listing stream.
677          */
678         numDirChunks = cm_NameEntries(dep->name, NULL);
679                 
680         /* compute the offset of the cookie representing the next entry */
681         nextEntryCookie = curOffset.LowPart
682             + (CM_DIR_CHUNKSIZE * numDirChunks);
683
684         if (dep->fid.vnode != 0) {
685             /* this is one of the entries to use: it is not deleted */
686             code = (*funcp)(scp, dep, parmp, &curOffset);
687             if (code) 
688                 break;
689         }       /* if we're including this name */
690                 
691       nextEntry:
692         /* and adjust curOffset to be where the new cookie is */
693         thyper.HighPart = 0;
694         thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
695         curOffset = LargeIntegerAdd(thyper, curOffset);
696     }           /* while copying data for dir listing */
697
698     /* release the mutex */
699     if (bufferp) {
700         lock_ReleaseMutex(&bufferp->mx);
701         buf_Release(bufferp);
702     }
703     return code;
704 }
705
706 int cm_NoneUpper(normchar_t *s)
707 {
708     normchar_t c;
709     while (c = *s++)
710         if (c >= 'A' && c <= 'Z')
711             return 0;
712     return 1;
713 }
714
715 int cm_NoneLower(normchar_t *s)
716 {
717     normchar_t c;
718     while (c = *s++)
719         if (c >= 'a' && c <= 'z')
720             return 0;
721     return 1;
722 }
723
724 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
725                          osi_hyper_t *offp)
726 {
727     cm_lookupSearch_t *sp;
728     int match;
729     normchar_t matchName[MAX_PATH];
730     int looking_for_short_name = FALSE;
731
732     sp = (cm_lookupSearch_t *) rockp;
733
734     if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
735         /* Can't normalize FS string. */
736         return 0;
737     }
738
739     if (sp->caseFold)
740         match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
741     else
742         match = cm_NormStrCmp(matchName, sp->nsearchNamep);
743
744     if (match != 0
745         && sp->hasTilde
746         && !cm_Is8Dot3(matchName)) {
747
748         cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
749         if (sp->caseFold)
750             match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
751         else
752             match = cm_NormStrCmp(matchName, sp->nsearchNamep);
753         looking_for_short_name = TRUE;
754     }
755
756     if (match != 0)
757         return 0;
758
759     sp->found = 1;
760     if (!sp->caseFold) 
761         sp->ExactFound = 1;
762
763     if (!sp->caseFold || looking_for_short_name) {
764         cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
765         return CM_ERROR_STOPNOW;
766     }
767
768     /*
769      * If we get here, we are doing a case-insensitive search, and we
770      * have found a match.  Now we determine what kind of match it is:
771      * exact, lower-case, upper-case, or none of the above.  This is done
772      * in order to choose among matches, if there are more than one.
773      */
774
775     /* Exact matches are the best. */
776     match = cm_NormStrCmp(matchName, sp->nsearchNamep);
777     if (match == 0) {
778         sp->ExactFound = 1;
779         cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
780         return CM_ERROR_STOPNOW;
781     }
782
783     /* Lower-case matches are next. */
784     if (sp->LCfound)
785         return 0;
786     if (cm_NoneUpper(matchName)) {
787         sp->LCfound = 1;
788         goto inexact;
789     }
790
791     /* Upper-case matches are next. */
792     if (sp->UCfound)
793         return 0;
794     if (cm_NoneLower(matchName)) {
795         sp->UCfound = 1;
796         goto inexact;
797     }
798
799     /* General matches are last. */
800     if (sp->NCfound)
801         return 0;
802     sp->NCfound = 1;
803
804   inexact:
805     cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
806     return 0;
807 }       
808
809 /* read the contents of a mount point into the appropriate string.
810  * called with write locked scp, and returns with locked scp.
811  */
812 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
813 {
814     long code;
815     cm_buf_t *bufp = NULL;
816     osi_hyper_t thyper;
817     int tlen;
818
819     if (scp->mountPointStringp[0]) 
820         return 0;
821         
822 #ifdef AFS_FREELANCE_CLIENT
823     /* File servers do not have data for freelance entries */
824     if (cm_freelanceEnabled &&
825         scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
826         scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
827     {       
828         code = cm_FreelanceFetchMountPointString(scp);
829     } else 
830 #endif /* AFS_FREELANCE_CLIENT */        
831     {
832         /* otherwise, we have to read it in */
833         lock_ReleaseWrite(&scp->rw);
834
835         thyper.LowPart = thyper.HighPart = 0;
836         code = buf_Get(scp, &thyper, reqp, &bufp);
837
838         lock_ObtainWrite(&scp->rw);
839         if (code)
840             return code;
841
842         while (1) {
843             code = cm_SyncOp(scp, bufp, userp, reqp, 0,
844                               CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
845             if (code)
846                 goto done;
847
848             cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
849
850             if (cm_HaveBuffer(scp, bufp, 0)) 
851                 break;
852
853             /* otherwise load buffer */
854             code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
855             if (code)
856                 goto done;
857         }
858         /* locked, has callback, has valid data in buffer */
859         if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1) 
860             return CM_ERROR_TOOBIG;
861         if (tlen <= 0) {
862             code = CM_ERROR_INVAL;
863             goto done;
864         }
865
866         /* someone else did the work while we were out */
867         if (scp->mountPointStringp[0]) {
868             code = 0;
869             goto done;
870         }
871
872         /* otherwise, copy out the link */
873         memcpy(scp->mountPointStringp, bufp->datap, tlen);
874
875         /* now make it null-terminated.  Note that the original contents of a
876          * link that is a mount point is "#volname." where "." is there just to
877          * be turned into a null.  That is, we can trash the last char of the
878          * link without damaging the vol name.  This is a stupid convention,
879          * but that's the protocol.
880          */
881         scp->mountPointStringp[tlen-1] = 0;
882         code = 0;
883
884       done:
885         if (bufp) 
886             buf_Release(bufp);
887     }
888     return code;
889 }
890
891
892 /* called with a locked scp and chases the mount point, yielding outScpp.
893  * scp remains write locked, just for simplicity of describing the interface.
894  */
895 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
896                          cm_req_t *reqp, cm_scache_t **outScpp)
897 {
898     fschar_t *cellNamep = NULL;
899     fschar_t *volNamep = NULL;
900     afs_uint32 code;
901     fschar_t *cp;
902     fschar_t *mpNamep;
903     cm_volume_t *volp = NULL;
904     cm_cell_t *cellp;
905     fschar_t mtType;
906     cm_fid_t tfid;
907     size_t vnLength;
908     int targetType;
909
910     *outScpp = NULL;
911
912     if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
913         tfid = scp->mountRootFid;
914         lock_ReleaseWrite(&scp->rw);
915         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
916         lock_ObtainWrite(&scp->rw);
917         return code;
918     }
919
920     /* parse the volume name */
921     mpNamep = scp->mountPointStringp;
922     if (!mpNamep[0])
923         return CM_ERROR_NOSUCHPATH;
924     mtType = *scp->mountPointStringp;
925
926     cp = cm_FsStrChr(mpNamep, _FS(':'));
927     if (cp) {
928         /* cellular mount point */
929         cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
930         cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
931         volNamep = cm_FsStrDup(cp+1);
932
933         /* now look up the cell */
934         lock_ReleaseWrite(&scp->rw);
935         cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
936         lock_ObtainWrite(&scp->rw);
937     } else {
938         /* normal mt pt */
939         volNamep = cm_FsStrDup(mpNamep + 1);
940
941 #ifdef AFS_FREELANCE_CLIENT
942         /* 
943          * Mount points in the Freelance cell should default
944          * to the workstation cell.
945          */
946         if (cm_freelanceEnabled &&
947              scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
948              scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
949         {       
950             fschar_t rootCellName[256]="";
951             cm_GetRootCellName(rootCellName);
952             cellp = cm_GetCell(rootCellName, 0);
953         } else 
954 #endif /* AFS_FREELANCE_CLIENT */        
955             cellp = cm_FindCellByID(scp->fid.cell, 0);
956     }
957
958     if (!cellp) {
959         code = CM_ERROR_NOSUCHCELL;
960         goto done;
961     }
962
963     vnLength = cm_FsStrLen(volNamep);
964     if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
965         targetType = BACKVOL;
966     else if (vnLength >= 10
967              && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
968         targetType = ROVOL;
969     else
970         targetType = RWVOL;
971
972     /* check for backups within backups */
973     if (targetType == BACKVOL
974          && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
975          == CM_SCACHEFLAG_RO) {
976         code = CM_ERROR_NOSUCHVOLUME;
977         goto done;
978     }
979
980     /* now we need to get the volume */
981     lock_ReleaseWrite(&scp->rw);
982     if (cm_VolNameIsID(volNamep)) {
983         code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp, 
984                                 CM_GETVOL_FLAG_CREATE, &volp);
985     } else {
986         code = cm_FindVolumeByName(cellp, volNamep, userp, reqp, 
987                                   CM_GETVOL_FLAG_CREATE, &volp);
988     }
989     lock_ObtainWrite(&scp->rw);
990         
991     if (code == 0) {
992         afs_uint32 cell, volume;
993         cm_vol_state_t *statep;
994
995         cell = cellp->cellID;
996         
997         /* if the mt pt originates in a .backup volume (not a .readonly)
998          * and FollowBackupPath is active, and if there is a .backup
999          * volume for the target, then use the .backup of the target
1000          * instead of the read-write.
1001          */
1002         if (cm_followBackupPath && 
1003             volp->vol[BACKVOL].ID != 0 &&
1004             (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
1005             (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
1006             ) {
1007             targetType = BACKVOL;
1008         } 
1009         /* if the mt pt is in a read-only volume (not just a
1010          * backup), and if there is a read-only volume for the
1011          * target, and if this is a targetType '#' mount point, use
1012          * the read-only, otherwise use the one specified.
1013          */
1014         else if (mtType == '#' && targetType == RWVOL && 
1015                  (scp->flags & CM_SCACHEFLAG_PURERO) && 
1016                  volp->vol[ROVOL].ID != 0) {
1017             targetType = ROVOL;
1018         }
1019
1020         lock_ObtainWrite(&volp->rw);
1021         statep = cm_VolumeStateByType(volp, targetType);
1022         volume = statep->ID;
1023         statep->dotdotFid = dscp->fid;
1024         lock_ReleaseWrite(&volp->rw);
1025
1026         /* the rest of the fid is a magic number */
1027         cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1028         scp->mountRootGen = cm_data.mountRootGen;
1029
1030         tfid = scp->mountRootFid;
1031         lock_ReleaseWrite(&scp->rw);
1032         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1033         lock_ObtainWrite(&scp->rw);
1034     }
1035
1036   done:
1037     if (volp)
1038         cm_PutVolume(volp);
1039     if (cellNamep)
1040         free(cellNamep);
1041     if (volNamep)
1042         free(volNamep);
1043     return code;
1044 }       
1045
1046 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1047                        cm_req_t *reqp, cm_scache_t **outScpp)
1048 {
1049     long code;
1050     int dnlcHit = 1;    /* did we hit in the dnlc? yes, we did */
1051     cm_scache_t *tscp = NULL;
1052     cm_scache_t *mountedScp;
1053     cm_lookupSearch_t rock;
1054     int getroot;
1055     normchar_t *nnamep = NULL;
1056     fschar_t *fnamep = NULL;
1057     size_t fnlen;
1058
1059     *outScpp = NULL;
1060
1061     memset(&rock, 0, sizeof(rock));
1062
1063     if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1064         && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1065         if (dscp->dotdotFid.volume == 0)
1066             return CM_ERROR_NOSUCHVOLUME;
1067         rock.fid = dscp->dotdotFid;
1068         goto haveFid;
1069     } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1070         rock.fid = dscp->fid;
1071         goto haveFid;
1072     }
1073
1074     nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1075     if (!nnamep) {
1076         code = CM_ERROR_NOSUCHFILE;
1077         goto done;
1078     }
1079     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1080     if (!fnamep) {
1081         code = CM_ERROR_NOSUCHFILE;
1082         goto done;
1083     }
1084
1085 retry_lookup:
1086     if (flags & CM_FLAG_NOMOUNTCHASE) {
1087         /* In this case, we should go and call cm_Dir* functions
1088            directly since the following cm_ApplyDir() function will
1089            not. */
1090
1091         cm_dirOp_t dirop;
1092 #ifdef USE_BPLUS
1093         int usedBplus = 0;
1094 #endif
1095
1096         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1097                              CM_DIROP_FLAG_NONE, &dirop);
1098         if (code == 0) {
1099 #ifdef USE_BPLUS
1100             code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1101             if (code != EINVAL)
1102                 usedBplus = 1;
1103             else
1104 #endif
1105                 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1106
1107             cm_EndDirOp(&dirop);
1108         }
1109
1110         if (code == 0) {
1111             /* found it */
1112             rock.found = TRUE;
1113             goto haveFid;
1114         }
1115 #ifdef USE_BPLUS
1116         if (usedBplus) {
1117             if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1118                 /* found it */
1119                 code = 0;
1120                 rock.found = TRUE;
1121                 goto haveFid;
1122             }
1123             
1124             code = CM_ERROR_BPLUS_NOMATCH;
1125             goto notfound;
1126         }
1127 #endif
1128     }
1129
1130     rock.fid.cell = dscp->fid.cell;
1131     rock.fid.volume = dscp->fid.volume;
1132     rock.searchNamep = fnamep;
1133     rock.nsearchNamep = nnamep;
1134     rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1135     rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1136
1137     /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1138     code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1139                        (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1140
1141     /* code == 0 means we fell off the end of the dir, while stopnow means
1142      * that we stopped early, probably because we found the entry we're
1143      * looking for.  Any other non-zero code is an error.
1144      */
1145     if (code && code != CM_ERROR_STOPNOW && code != CM_ERROR_BPLUS_NOMATCH) {
1146         /* if the cm_scache_t we are searching in is not a directory 
1147          * we must return path not found because the error 
1148          * is to describe the final component not an intermediary
1149          */
1150         if (code == CM_ERROR_NOTDIR) {
1151             if (flags & CM_FLAG_CHECKPATH)
1152                 code = CM_ERROR_NOSUCHPATH;
1153             else
1154                 code = CM_ERROR_NOSUCHFILE;
1155         }
1156         goto done;
1157     }
1158
1159 notfound:
1160     getroot = (dscp==cm_data.rootSCachep) ;
1161     if (!rock.found) {
1162         if (!cm_freelanceEnabled || !getroot) {
1163             if (flags & CM_FLAG_CHECKPATH)
1164                 code = CM_ERROR_NOSUCHPATH;
1165             else
1166                 code = CM_ERROR_NOSUCHFILE;
1167             goto done;
1168         }
1169         else if (!cm_ClientStrChr(cnamep, '#') &&
1170                  !cm_ClientStrChr(cnamep, '%') &&
1171                  cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1172                  cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1173                  cm_ClientStrCmpI(cnamep, _C("ipc$"))) 
1174         {
1175             /* nonexistent dir on freelance root, so add it */
1176             fschar_t fullname[CELL_MAXNAMELEN + 1] = ".";  /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1177             int  found = 0;
1178             int  retry = 0;
1179
1180             osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S", 
1181                      osi_LogSaveClientString(afsd_logp,cnamep));
1182
1183             /* 
1184              * There is an ugly behavior where a share name "foo" will be searched
1185              * for as "fo".  If the searched for name differs by an already existing
1186              * symlink or mount point in the Freelance directory, do not add the 
1187              * new value automatically.
1188              */
1189
1190             code = -1;
1191             fnlen = strlen(fnamep);
1192             if ( fnamep[fnlen-1] == '.') {
1193                 fnamep[fnlen-1] = '\0';
1194                 fnlen--;
1195                 retry = 1;
1196             }
1197
1198             if (cnamep[0] == '.') {
1199                 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1200                     found = 1;
1201                     code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.", 1, &rock.fid);
1202                     if ( cm_FsStrCmpI(&fnamep[1], &fullname[1])) {
1203                         /*
1204                          * Do not permit symlinks that are one of:
1205                          *  . the cellname followed by a dot
1206                          *  . the cellname minus a single character
1207                          *  . a substring of the cellname that does not consist of full components
1208                          */
1209                         if ( cm_strnicmp_utf8(&fnamep[1], fullname, (int)fnlen-1) == 0 &&
1210                              (fnlen-1 == strlen(fullname)-1 || fullname[fnlen-1] != '.'))
1211                         {
1212                             /* do not add; substitute fullname for the search */
1213                             free(fnamep);
1214                             fnamep = malloc(strlen(fullname)+2);
1215                             fnamep[0] = '.';
1216                             strncpy(&fnamep[1], fullname, strlen(fullname)+1);
1217                             retry = 1;
1218                         } else {
1219                             code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1220                         }
1221                     }
1222                 }
1223             } else {
1224                 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1225                     found = 1;
1226                     code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
1227                     if ( cm_FsStrCmpI(fnamep, fullname)) {
1228                         /*
1229                          * Do not permit symlinks that are one of:
1230                          *  . the cellname followed by a dot
1231                          *  . the cellname minus a single character
1232                          *  . a substring of the cellname that does not consist of full components
1233                          */
1234                         if ( cm_strnicmp_utf8(fnamep, fullname, (int)fnlen-1) == 0 &&
1235                              (fnlen == strlen(fullname)-1 || fullname[fnlen] != '.'))
1236                         {
1237                             /* do not add; substitute fullname for the search */
1238                                 free(fnamep);
1239                                 fnamep = strdup(fullname);
1240                                 code = 0;
1241                                 retry = 1;
1242                         } else {
1243                             code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1244                         }
1245                     }
1246                 }
1247             }
1248
1249             if (retry) {
1250                 if (nnamep)
1251                     free(nnamep);
1252                 nnamep = cm_FsStringToNormStringAlloc(fnamep, -1, NULL);
1253                 goto retry_lookup;
1254             }
1255
1256             if (!found || code) {   /* add mount point failed, so give up */
1257                 if (flags & CM_FLAG_CHECKPATH)
1258                     code = CM_ERROR_NOSUCHPATH;
1259                 else
1260                     code = CM_ERROR_NOSUCHFILE;
1261                 goto done;
1262             }
1263             tscp = NULL;   /* to force call of cm_GetSCache */
1264         } else {
1265             if (flags & CM_FLAG_CHECKPATH)
1266                 code = CM_ERROR_NOSUCHPATH;
1267             else
1268                 code = CM_ERROR_NOSUCHFILE;
1269             goto done;
1270         }
1271     }
1272
1273   haveFid:
1274     if ( !tscp )    /* we did not find it in the dnlc */
1275     {
1276         dnlcHit = 0; 
1277         code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1278         if (code) 
1279             goto done;
1280     }
1281     /* tscp is now held */
1282
1283     lock_ObtainWrite(&tscp->rw);
1284     code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1285                       CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1286     if (code) { 
1287         lock_ReleaseWrite(&tscp->rw);
1288         cm_ReleaseSCache(tscp);
1289         goto done;
1290     }
1291     cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1292     /* tscp is now locked */
1293
1294     if (!(flags & CM_FLAG_NOMOUNTCHASE)
1295          && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1296         /* mount points are funny: they have a volume name to mount
1297          * the root of.
1298          */
1299         code = cm_ReadMountPoint(tscp, userp, reqp);
1300         if (code == 0)
1301             code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1302                                        &mountedScp);
1303         lock_ReleaseWrite(&tscp->rw);
1304         cm_ReleaseSCache(tscp);
1305         if (code)
1306             goto done;
1307
1308         tscp = mountedScp;
1309     }
1310     else {
1311         lock_ReleaseWrite(&tscp->rw);
1312     }
1313
1314     /* copy back pointer */
1315     *outScpp = tscp;
1316
1317     /* insert scache in dnlc */
1318     if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1319         /* lock the directory entry to prevent racing callback revokes */
1320         lock_ObtainRead(&dscp->rw);
1321         if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1322             /* TODO: reuse nnamep from above */
1323             if (nnamep) 
1324                 free(nnamep);
1325             nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1326             if (nnamep)
1327                 cm_dnlcEnter(dscp, nnamep, tscp);
1328         }
1329         lock_ReleaseRead(&dscp->rw);
1330     }
1331
1332     /* and return */
1333   done:
1334     if (fnamep) {
1335         free (fnamep);
1336         fnamep = NULL;
1337     }
1338     if (nnamep) {
1339         free (nnamep);
1340         nnamep = NULL;
1341     }
1342
1343     return code;
1344 }
1345
1346 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1347 {
1348     clientchar_t *tp;
1349     int prefixCount;
1350
1351     tp = cm_ClientStrRChr(inp, '@');
1352     if (tp == NULL) 
1353         return 0;               /* no @sys */
1354
1355     if (cm_ClientStrCmp(tp, _C("@sys")) != 0) 
1356         return 0;       /* no @sys */
1357
1358     /* caller just wants to know if this is a valid @sys type of name */
1359     if (outp == NULL) 
1360         return 1;
1361
1362     if (index >= cm_sysNameCount)
1363         return -1;
1364
1365     /* otherwise generate the properly expanded @sys name */
1366     prefixCount = (int)(tp - inp);
1367
1368     cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount);       /* copy out "a." from "a.@sys" */
1369     outp[prefixCount] = 0;              /* null terminate the "a." */
1370     cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1371     return 1;
1372 }   
1373
1374 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1375                                 cm_req_t *reqp, cm_scache_t ** outScpp)
1376 {
1377     afs_uint32    code = 0;
1378     fschar_t      cellName[CELL_MAXNAMELEN];
1379     fschar_t      volumeName[VL_MAXNAMELEN];
1380     size_t        len;
1381     fschar_t *        cp;
1382     fschar_t *        tp;
1383     fschar_t *        fnamep = NULL;
1384
1385     cm_cell_t *   cellp = NULL;
1386     cm_volume_t * volp = NULL;
1387     cm_fid_t      fid;
1388     afs_uint32    volume;
1389     int           volType;
1390     int           mountType = RWVOL;
1391
1392     osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1393              osi_LogSaveClientString(afsd_logp, namep));
1394
1395     if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1396         goto _exit_invalid_path;
1397     }
1398
1399     /* namep is assumed to look like the following:
1400
1401        @vol:<cellname>%<volume>\0
1402        or
1403        @vol:<cellname>#<volume>\0
1404
1405      */
1406
1407     fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1408     cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1409     tp = cm_FsStrChr(cp, '%');
1410     if (tp == NULL)
1411         tp = cm_FsStrChr(cp, '#');
1412     if (tp == NULL ||
1413         (len = tp - cp) == 0 ||
1414         len > CELL_MAXNAMELEN)
1415         goto _exit_invalid_path;
1416     cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1417
1418     if (*tp == '#')
1419         mountType = ROVOL;
1420
1421     cp = tp+1;                  /* cp now points to volume, supposedly */
1422     cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1423
1424     /* OK, now we have the cell and the volume */
1425     osi_Log2(afsd_logp, "   Found cell [%s] and volume [%s]",
1426              osi_LogSaveFsString(afsd_logp, cellName),
1427              osi_LogSaveFsString(afsd_logp, volumeName));
1428
1429     cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1430     if (cellp == NULL) {
1431         goto _exit_invalid_path;
1432     }
1433
1434     len = cm_FsStrLen(volumeName);
1435     if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1436         volType = BACKVOL;
1437     else if (len >= 10 &&
1438              cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1439         volType = ROVOL;
1440     else
1441         volType = RWVOL;
1442
1443     if (cm_VolNameIsID(volumeName)) {
1444         code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1445                                 CM_GETVOL_FLAG_CREATE, &volp);
1446     } else {
1447         code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1448                                   CM_GETVOL_FLAG_CREATE, &volp);
1449     }
1450
1451     if (code != 0)
1452         goto _exit_cleanup;
1453
1454     if (volType == BACKVOL)
1455         volume = volp->vol[BACKVOL].ID;
1456     else if (volType == ROVOL ||
1457              (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1458         volume = volp->vol[ROVOL].ID;
1459     else
1460         volume = volp->vol[RWVOL].ID;
1461
1462     cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1463
1464     code = cm_GetSCache(&fid, outScpp, userp, reqp);
1465
1466   _exit_cleanup:
1467     if (fnamep)
1468         free(fnamep);
1469
1470     if (volp)
1471         cm_PutVolume(volp);
1472
1473     if (code == 0)
1474         return code;
1475
1476  _exit_invalid_path:
1477     if (flags & CM_FLAG_CHECKPATH)
1478         return CM_ERROR_NOSUCHPATH;
1479     else
1480         return CM_ERROR_NOSUCHFILE;
1481 }
1482
1483 #ifdef DEBUG_REFCOUNT
1484 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1485                cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1486 #else
1487 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1488                cm_req_t *reqp, cm_scache_t **outScpp)
1489 #endif
1490 {
1491     long code;
1492     clientchar_t tname[AFSPATHMAX];
1493     int sysNameIndex = 0;
1494     cm_scache_t *scp = NULL;
1495
1496 #ifdef DEBUG_REFCOUNT
1497     afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1498     osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1499 #endif
1500
1501     if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1502         if (flags & CM_FLAG_CHECKPATH)
1503             return CM_ERROR_NOSUCHPATH;
1504         else
1505             return CM_ERROR_NOSUCHFILE;
1506     }
1507
1508     if (dscp == cm_data.rootSCachep &&
1509         cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1510         return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1511     }
1512
1513     if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1514         for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1515             code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1516             if (code > 0) {
1517                 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1518 #ifdef DEBUG_REFCOUNT
1519                 afsi_log("%s:%d cm_LookupInternal (1) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1520                 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1521 #endif
1522
1523                 if (code == 0) {
1524                     *outScpp = scp;
1525                     return 0;
1526                 }
1527                 if (scp) {
1528                     cm_ReleaseSCache(scp);
1529                     scp = NULL;
1530                 }
1531             } else {
1532                 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1533 #ifdef DEBUG_REFCOUNT
1534                 afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1535                 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1536 #endif
1537                 *outScpp = scp;
1538                 return code;
1539             }
1540         }
1541     } else {
1542         code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1543 #ifdef DEBUG_REFCOUNT
1544         afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1545         osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1546 #endif
1547         *outScpp = scp;
1548         return code;
1549     }
1550
1551     /* None of the possible sysName expansions could be found */
1552     if (flags & CM_FLAG_CHECKPATH)
1553         return CM_ERROR_NOSUCHPATH;
1554     else
1555         return CM_ERROR_NOSUCHFILE;
1556 }
1557
1558 /*! \brief Unlink a file name
1559
1560   Encapsulates a call to RXAFS_RemoveFile().
1561
1562   \param[in] dscp cm_scache_t pointing at the directory containing the
1563       name to be unlinked.
1564
1565   \param[in] fnamep Original name to be unlinked.  This is the
1566       name that will be passed into the RXAFS_RemoveFile() call.
1567       This parameter is optional.  If not provided, the value will
1568       be looked up.
1569
1570   \param[in] came Client name to be unlinked.  This name will be used
1571       to update the local directory caches.
1572
1573   \param[in] userp cm_user_t for the request.
1574
1575   \param[in] reqp Request tracker.
1576  
1577  */
1578 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1579                cm_user_t *userp, cm_req_t *reqp)
1580 {
1581     long code;
1582     cm_conn_t *connp;
1583     AFSFid afsFid;
1584     int sflags;
1585     AFSFetchStatus newDirStatus;
1586     AFSVolSync volSync;
1587     struct rx_connection * rxconnp;
1588     cm_dirOp_t dirop;
1589     cm_scache_t *scp = NULL;
1590     int free_fnamep = FALSE;
1591
1592     memset(&volSync, 0, sizeof(volSync));
1593
1594     if (fnamep == NULL) {
1595         code = -1;
1596 #ifdef USE_BPLUS
1597         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1598                              CM_DIROP_FLAG_NONE, &dirop);
1599         if (code == 0) {
1600             code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1601             if (code == 0)
1602                 free_fnamep = TRUE;
1603             cm_EndDirOp(&dirop);
1604         }
1605 #endif
1606         if (code)
1607             goto done;
1608     }
1609
1610 #ifdef AFS_FREELANCE_CLIENT
1611     if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1612         /* deleting a mount point from the root dir. */
1613         code = cm_FreelanceRemoveMount(fnamep);
1614         goto done;
1615     }
1616 #endif  
1617
1618     code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1619
1620     /* make sure we don't screw up the dir status during the merge */
1621     code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE,
1622                          CM_DIROP_FLAG_NONE, &dirop);
1623
1624     lock_ObtainWrite(&dscp->rw);
1625     sflags = CM_SCACHESYNC_STOREDATA;
1626     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1627     lock_ReleaseWrite(&dscp->rw);
1628     if (code) {
1629         cm_EndDirOp(&dirop);
1630         goto done;
1631     }
1632
1633     /* make the RPC */
1634     afsFid.Volume = dscp->fid.volume;
1635     afsFid.Vnode = dscp->fid.vnode;
1636     afsFid.Unique = dscp->fid.unique;
1637
1638     osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1639     do {
1640         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1641         if (code)
1642             continue;
1643
1644         rxconnp = cm_GetRxConn(connp);
1645         code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1646                                 &newDirStatus, &volSync);
1647         rx_PutConnection(rxconnp);
1648
1649     } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1650     code = cm_MapRPCError(code, reqp);
1651
1652     if (code)
1653         osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1654     else
1655         osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1656
1657     if (dirop.scp) {
1658         lock_ObtainWrite(&dirop.scp->dirlock);
1659         dirop.lockType = CM_DIRLOCK_WRITE;
1660     }
1661     lock_ObtainWrite(&dscp->rw);
1662     cm_dnlcRemove(dscp, cnamep);
1663     cm_SyncOpDone(dscp, NULL, sflags);
1664     if (code == 0) {
1665         cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1666     } else if (code == CM_ERROR_NOSUCHFILE) {
1667         /* windows would not have allowed the request to delete the file 
1668          * if it did not believe the file existed.  therefore, we must 
1669          * have an inconsistent view of the world.
1670          */
1671         dscp->cbServerp = NULL;
1672     }
1673     lock_ReleaseWrite(&dscp->rw);
1674
1675     if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1676         cm_DirDeleteEntry(&dirop, fnamep);
1677 #ifdef USE_BPLUS
1678         cm_BPlusDirDeleteEntry(&dirop, cnamep);
1679 #endif
1680     }
1681     cm_EndDirOp(&dirop);
1682
1683     if (scp) {
1684         cm_ReleaseSCache(scp);
1685         if (code == 0) {
1686             lock_ObtainWrite(&scp->rw);
1687             if (--scp->linkCount == 0)
1688                 scp->flags |= CM_SCACHEFLAG_DELETED;
1689             cm_DiscardSCache(scp);
1690             lock_ReleaseWrite(&scp->rw);
1691         }
1692     }
1693
1694   done:
1695     if (free_fnamep)
1696         free(fnamep);
1697
1698     return code;
1699 }
1700
1701 /* called with a write locked vnode, and fills in the link info.
1702  * returns this the vnode still write locked.
1703  */
1704 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1705 {
1706     long code;
1707     cm_buf_t *bufp;
1708     long temp;
1709     osi_hyper_t thyper;
1710
1711     lock_AssertWrite(&linkScp->rw);
1712     if (!linkScp->mountPointStringp[0]) {
1713         
1714 #ifdef AFS_FREELANCE_CLIENT
1715         /* File servers do not have data for freelance entries */
1716         if (cm_freelanceEnabled &&
1717             linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1718             linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1719         {
1720             code = cm_FreelanceFetchMountPointString(linkScp);
1721         } else 
1722 #endif /* AFS_FREELANCE_CLIENT */        
1723         {
1724             /* read the link data from the file server*/
1725             lock_ReleaseWrite(&linkScp->rw);
1726             thyper.LowPart = thyper.HighPart = 0;
1727             code = buf_Get(linkScp, &thyper, reqp, &bufp);
1728             lock_ObtainWrite(&linkScp->rw);
1729             if (code) 
1730                 return code;
1731             while (1) {
1732                 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1733                                   CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1734                 if (code) {
1735                     buf_Release(bufp);
1736                     return code;
1737                 }
1738                 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1739
1740                 if (cm_HaveBuffer(linkScp, bufp, 0)) 
1741                     break;
1742
1743                 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1744                 if (code) {
1745                     buf_Release(bufp);
1746                     return code;
1747                 }
1748             } /* while loop to get the data */
1749
1750             /* now if we still have no link read in,
1751              * copy the data from the buffer */
1752             if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1753                 buf_Release(bufp);
1754                 return CM_ERROR_TOOBIG;
1755             }       
1756
1757             /* otherwise, it fits; make sure it is still null (could have
1758              * lost race with someone else referencing this link above),
1759              * and if so, copy in the data.
1760              */
1761             if (!linkScp->mountPointStringp[0]) {
1762                 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1763                 linkScp->mountPointStringp[temp] = 0;   /* null terminate */
1764             }
1765             buf_Release(bufp);
1766         }
1767         
1768         if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1769             linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1770
1771     }   /* don't have sym link contents cached */
1772
1773     return 0;
1774 }       
1775
1776 /* called with a held vnode and a path suffix, with the held vnode being a
1777  * symbolic link.  Our goal is to generate a new path to interpret, and return
1778  * this new path in newSpaceBufferp.  If the new vnode is relative to a dir
1779  * other than the directory containing the symbolic link, then the new root is
1780  * returned in *newRootScpp, otherwise a null is returned there.
1781  */
1782 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1783                      cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1784                      cm_user_t *userp, cm_req_t *reqp)
1785 {
1786     long code = 0;
1787     long len;
1788     fschar_t *linkp;
1789     cm_space_t *tsp;
1790
1791     *newRootScpp = NULL;
1792     *newSpaceBufferp = NULL;
1793
1794     lock_ObtainWrite(&linkScp->rw);
1795     code = cm_HandleLink(linkScp, userp, reqp);
1796     if (code)
1797         goto done;
1798
1799     /* if we may overflow the buffer, bail out; buffer is signficantly
1800      * bigger than max path length, so we don't really have to worry about
1801      * being a little conservative here.
1802      */
1803     if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1804         >= CM_UTILS_SPACESIZE) {
1805         code = CM_ERROR_TOOBIG;
1806         goto done;
1807     }
1808
1809     tsp = cm_GetSpace();
1810     linkp = linkScp->mountPointStringp;
1811     if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1812         if (strlen(linkp) > cm_mountRootLen)
1813             StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1814         else
1815             tsp->data[0] = 0;
1816         *newRootScpp = cm_RootSCachep(userp, reqp);
1817         cm_HoldSCache(*newRootScpp);
1818     } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1819         if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName)))) 
1820         {
1821             char * p = &linkp[len + 3];
1822             if (strnicmp(p, "all", 3) == 0)
1823                 p += 4;
1824
1825             StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1826             for (p = tsp->data; *p; p++) {
1827                 if (*p == '\\')
1828                     *p = '/';
1829             }
1830             *newRootScpp = cm_RootSCachep(userp, reqp);
1831             cm_HoldSCache(*newRootScpp);
1832         } else {
1833             linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1834             StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1835             code = CM_ERROR_PATH_NOT_COVERED;
1836         }
1837     } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1838                 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1839         linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1840         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1841         code = CM_ERROR_PATH_NOT_COVERED;
1842     } else if (*linkp == '\\' || *linkp == '/') {
1843 #if 0   
1844         /* formerly, this was considered to be from the AFS root,
1845          * but this seems to create problems.  instead, we will just
1846          * reject the link */
1847         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1848         *newRootScpp = cm_RootSCachep(userp, reqp);
1849         cm_HoldSCache(*newRootScpp);
1850 #else
1851         /* we still copy the link data into the response so that 
1852          * the user can see what the link points to
1853          */
1854         linkScp->fileType = CM_SCACHETYPE_INVALID;
1855         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1856         code = CM_ERROR_NOSUCHPATH;
1857 #endif  
1858     } else {
1859         /* a relative link */
1860         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1861     }
1862     if (pathSuffixp[0] != 0) {  /* if suffix string is non-null */
1863         StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1864         StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1865     }
1866
1867     if (code == 0) {
1868         clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1869         if (cpath != NULL) {
1870         cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1871         free(cpath);
1872         *newSpaceBufferp = tsp;
1873     } else {
1874             code = CM_ERROR_NOSUCHPATH;
1875         }
1876     } 
1877
1878     if (code != 0) {
1879         cm_FreeSpace(tsp);
1880
1881         if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1882             cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1883         }
1884     }
1885
1886  done:
1887     lock_ReleaseWrite(&linkScp->rw);
1888     return code;
1889 }
1890 #ifdef DEBUG_REFCOUNT
1891 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1892                  cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1893                  cm_scache_t **outScpp, 
1894                  char * file, long line)
1895 #else
1896 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1897               cm_user_t *userp, clientchar_t *tidPathp,
1898               cm_req_t *reqp, cm_scache_t **outScpp)
1899 #endif
1900 {
1901     long code;
1902     clientchar_t *tp;                   /* ptr moving through input buffer */
1903     clientchar_t tc;                    /* temp char */
1904     int haveComponent;          /* has new component started? */
1905     clientchar_t component[AFSPATHMAX]; /* this is the new component */
1906     clientchar_t *cp;                   /* component name being assembled */
1907     cm_scache_t *tscp;          /* current location in the hierarchy */
1908     cm_scache_t *nscp;          /* next dude down */
1909     cm_scache_t *dirScp;        /* last dir we searched */
1910     cm_scache_t *linkScp;       /* new root for the symlink we just
1911     * looked up */
1912     cm_space_t *psp;            /* space for current path, if we've hit
1913     * any symlinks */
1914     cm_space_t *tempsp;         /* temp vbl */
1915     clientchar_t *restp;                /* rest of the pathname to interpret */
1916     int symlinkCount;           /* count of # of symlinks traversed */
1917     int extraFlag;              /* avoid chasing mt pts for dir cmd */
1918     int phase = 1;              /* 1 = tidPathp, 2 = pathp */
1919 #define MAX_FID_COUNT 512
1920     cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1921     int fid_count = 0;          /* number of fids processed in this path walk */
1922     int i;
1923
1924     *outScpp = NULL;
1925
1926 #ifdef DEBUG_REFCOUNT
1927     afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1928     osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1929              rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>", 
1930              flags);
1931 #endif
1932
1933     tp = tidPathp;
1934     if (tp == NULL) {
1935         tp = pathp;
1936         phase = 2;
1937     }
1938     if (tp == NULL) {
1939         tp = _C("");
1940     }
1941     haveComponent = 0;
1942     psp = NULL;
1943     tscp = rootSCachep;
1944     cm_HoldSCache(tscp);
1945     symlinkCount = 0;
1946     dirScp = NULL;
1947
1948
1949     while (1) {
1950         tc = *tp++;
1951
1952         /* map Unix slashes into DOS ones so we can interpret Unix
1953          * symlinks properly
1954          */
1955         if (tc == '/') 
1956             tc = '\\';
1957
1958         if (!haveComponent) {
1959             if (tc == '\\') {
1960                 continue;
1961             } else if (tc == 0) {
1962                 if (phase == 1) {
1963                     phase = 2;
1964                     tp = pathp;
1965                     continue;
1966                 }
1967                 code = 0;
1968                 break;
1969             } else {
1970                 haveComponent = 1;
1971                 cp = component;
1972                 *cp++ = tc;
1973             }
1974         } else {
1975             /* we have a component here */
1976             if (tc == 0 || tc == '\\') {
1977                 /* end of the component; we're at the last
1978                  * component if tc == 0.  However, if the last
1979                  * is a symlink, we have more to do.
1980                  */
1981                 *cp++ = 0;      /* add null termination */
1982                 extraFlag = 0;
1983                 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1984                     extraFlag = CM_FLAG_NOMOUNTCHASE;
1985                 code = cm_Lookup(tscp, component,
1986                                  flags | extraFlag,
1987                                  userp, reqp, &nscp);
1988
1989                 if (code == 0) {
1990                     if (!cm_ClientStrCmp(component,_C("..")) ||
1991                         !cm_ClientStrCmp(component,_C("."))) {
1992                         /* 
1993                          * roll back the fid list until we find the
1994                          * fid that matches where we are now.  Its not
1995                          * necessarily one or two fids because they
1996                          * might have been symlinks or mount points or
1997                          * both that were crossed.
1998                          */
1999                         for ( i=fid_count-1; i>=0; i--) {
2000                             if (!cm_FidCmp(&nscp->fid, &fids[i]))
2001                                 break;
2002                         }
2003                         fid_count = i+1;
2004                     } else {
2005                         /* add the new fid to the list */
2006                         if (fid_count == MAX_FID_COUNT) {
2007                             code = CM_ERROR_TOO_MANY_SYMLINKS;
2008                             cm_ReleaseSCache(nscp);
2009                             nscp = NULL;
2010                             break;
2011                         }       
2012                         fids[fid_count++] = nscp->fid;
2013                     }
2014                 }
2015
2016                 if (code) {
2017                     cm_ReleaseSCache(tscp);
2018                     if (dirScp)
2019                         cm_ReleaseSCache(dirScp);
2020                     if (psp) 
2021                         cm_FreeSpace(psp);
2022                     if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) && 
2023                         tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2024                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
2025                         return CM_ERROR_NOSUCHPATH;
2026                     } else {
2027                         osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
2028                         return code;
2029                     }
2030                 }
2031
2032                 haveComponent = 0;      /* component done */
2033                 if (dirScp)
2034                     cm_ReleaseSCache(dirScp);
2035                 dirScp = tscp;          /* for some symlinks */
2036                 tscp = nscp;            /* already held */
2037                 nscp = NULL;
2038                 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
2039                     code = 0;
2040                     if (dirScp) {
2041                         cm_ReleaseSCache(dirScp);
2042                         dirScp = NULL;
2043                     }
2044                     break;
2045                 }
2046
2047                 /* now, if tscp is a symlink, we should follow it and
2048                  * assemble the path again.
2049                  */
2050                 lock_ObtainWrite(&tscp->rw);
2051                 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2052                                   CM_SCACHESYNC_GETSTATUS
2053                                   | CM_SCACHESYNC_NEEDCALLBACK);
2054                 if (code) {
2055                     lock_ReleaseWrite(&tscp->rw);
2056                     cm_ReleaseSCache(tscp);
2057                     tscp = NULL;
2058                     if (dirScp) {
2059                         cm_ReleaseSCache(dirScp);
2060                         dirScp = NULL;
2061                     }
2062                     break;
2063                 }
2064                 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2065
2066                 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2067                     /* this is a symlink; assemble a new buffer */
2068                     lock_ReleaseWrite(&tscp->rw);
2069                     if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2070                         cm_ReleaseSCache(tscp);
2071                         tscp = NULL;
2072                         if (dirScp) {
2073                             cm_ReleaseSCache(dirScp);
2074                             dirScp = NULL;
2075                         }
2076                         if (psp) 
2077                             cm_FreeSpace(psp);
2078                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2079                         return CM_ERROR_TOO_MANY_SYMLINKS;
2080                     }
2081                     if (tc == 0) 
2082                         restp = _C("");
2083                     else 
2084                         restp = tp;
2085
2086                     {
2087                         fschar_t * frestp;
2088
2089                         /* TODO: make this better */
2090                         frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2091                         code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2092                         free(frestp);
2093                     }
2094
2095                     if (code == 0 && linkScp != NULL) {
2096                         if (linkScp == cm_data.rootSCachep) {
2097                             fid_count = 0;
2098                             i = 0;
2099                         } else {
2100                             for ( i=0; i<fid_count; i++) {
2101                                 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2102                                     code = CM_ERROR_TOO_MANY_SYMLINKS;
2103                                     cm_ReleaseSCache(linkScp);
2104                                     nscp = NULL;
2105                                     break;
2106                                 }
2107                             }
2108                         }
2109                         if (i == fid_count && fid_count < MAX_FID_COUNT) {
2110                             fids[fid_count++] = linkScp->fid;
2111                         }
2112                     }
2113
2114                     if (code) {
2115                         /* something went wrong */
2116                         cm_ReleaseSCache(tscp);
2117                         tscp = NULL;
2118                         if (dirScp) {
2119                             cm_ReleaseSCache(dirScp);
2120                             dirScp = NULL;
2121                         }
2122                         break;
2123                     }
2124
2125                     /* otherwise, tempsp has the new path,
2126                      * and linkScp is the new root from
2127                      * which to interpret that path.
2128                      * Continue with the namei processing,
2129                      * also doing the bookkeeping for the
2130                      * space allocation and tracking the
2131                      * vnode reference counts.
2132                      */
2133                     if (psp) 
2134                         cm_FreeSpace(psp);
2135                     psp = tempsp;
2136                     tp = psp->wdata;
2137                     cm_ReleaseSCache(tscp);
2138                     tscp = linkScp;
2139                     linkScp = NULL;
2140                     /* already held
2141                      * by AssembleLink
2142                      * now, if linkScp is null, that's
2143                      * AssembleLink's way of telling us that
2144                      * the sym link is relative to the dir
2145                      * containing the link.  We have a ref
2146                      * to it in dirScp, and we hold it now
2147                      * and reuse it as the new spot in the
2148                      * dir hierarchy.
2149                      */
2150                     if (tscp == NULL) {
2151                         tscp = dirScp;
2152                         dirScp = NULL;
2153                     }
2154                 } else {
2155                     /* not a symlink, we may be done */
2156                     lock_ReleaseWrite(&tscp->rw);
2157                     if (tc == 0) {
2158                         if (phase == 1) {
2159                             phase = 2;
2160                             tp = pathp;
2161                             continue;
2162                         }
2163                         if (dirScp) {
2164                             cm_ReleaseSCache(dirScp);
2165                             dirScp = NULL;
2166                         }
2167                         code = 0;
2168                         break;
2169                     }
2170                 }
2171                 if (dirScp) {
2172                     cm_ReleaseSCache(dirScp);
2173                     dirScp = NULL;
2174                 }
2175             } /* end of a component */
2176             else 
2177                 *cp++ = tc;
2178         } /* we have a component */
2179     } /* big while loop over all components */
2180
2181     /* already held */
2182     if (dirScp)
2183         cm_ReleaseSCache(dirScp);
2184     if (psp) 
2185         cm_FreeSpace(psp);
2186     if (code == 0) 
2187         *outScpp = tscp;
2188     else if (tscp)
2189         cm_ReleaseSCache(tscp);
2190
2191 #ifdef DEBUG_REFCOUNT
2192     afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2193 #endif
2194     osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2195     return code;
2196 }
2197
2198 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2199  * We chase the link, and return a held pointer to the target, if it exists,
2200  * in *outScpp.  If we succeed, we return 0, otherwise we return an error code
2201  * and do not hold or return a target vnode.
2202  *
2203  * This is very similar to calling cm_NameI with the last component of a name,
2204  * which happens to be a symlink, except that we've already passed by the name.
2205  *
2206  * This function is typically called by the directory listing functions, which
2207  * encounter symlinks but need to return the proper file length so programs
2208  * like "more" work properly when they make use of the attributes retrieved from
2209  * the dir listing.
2210  *
2211  * The input vnode should not be locked when this function is called.
2212  */
2213 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2214                          cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2215 {
2216     long code;
2217     cm_space_t *spacep;
2218     cm_scache_t *newRootScp;
2219
2220     *outScpp = NULL;
2221
2222     osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2223
2224     code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2225     if (code) 
2226         return code;
2227
2228     /* now, if newRootScp is NULL, we're really being told that the symlink
2229      * is relative to the current directory (dscp).
2230      */
2231     if (newRootScp == NULL) {
2232         newRootScp = dscp;
2233         cm_HoldSCache(dscp);
2234     }
2235
2236     code = cm_NameI(newRootScp, spacep->wdata,
2237                     CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2238                     userp, NULL, reqp, outScpp);
2239
2240     if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2241         code = CM_ERROR_NOSUCHPATH;
2242
2243     /* this stuff is allocated no matter what happened on the namei call,
2244      * so free it */
2245     cm_FreeSpace(spacep);
2246     cm_ReleaseSCache(newRootScp);
2247
2248     if (linkScp == *outScpp) {
2249         cm_ReleaseSCache(*outScpp);
2250         *outScpp = NULL;
2251         code = CM_ERROR_NOSUCHPATH;
2252     }
2253
2254     return code;
2255 }
2256
2257 /* for a given entry, make sure that it isn't in the stat cache, and then
2258  * add it to the list of file IDs to be obtained.
2259  *
2260  * Don't bother adding it if we already have a vnode.  Note that the dir
2261  * is locked, so we have to be careful checking the vnode we're thinking of
2262  * processing, to avoid deadlocks.
2263  */
2264 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2265                      osi_hyper_t *offp)
2266 {
2267     osi_hyper_t thyper;
2268     cm_bulkStat_t *bsp;
2269     int i;
2270     cm_scache_t *tscp;
2271     cm_fid_t tfid;
2272
2273     bsp = rockp;
2274
2275     /* Don't overflow bsp. */
2276     if (bsp->counter >= CM_BULKMAX)
2277         return CM_ERROR_STOPNOW;
2278
2279     thyper.LowPart = cm_data.buf_blockSize;
2280     thyper.HighPart = 0;
2281     thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2282
2283     /* thyper is now the first byte past the end of the record we're
2284      * interested in, and bsp->bufOffset is the first byte of the record
2285      * we're interested in.
2286      * Skip data in the others.
2287      * Skip '.' and '..'
2288      */
2289     if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2290         return 0;
2291     if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2292         return CM_ERROR_STOPNOW;
2293     if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2294         return 0;
2295
2296     cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2297     tscp = cm_FindSCache(&tfid);
2298     if (tscp) {
2299         if (lock_TryWrite(&tscp->rw)) {
2300             /* we have an entry that we can look at */
2301             if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2302                 /* we have a callback on it.  Don't bother
2303                  * fetching this stat entry, since we're happy
2304                  * with the info we have.
2305                  */
2306                 lock_ReleaseWrite(&tscp->rw);
2307                 cm_ReleaseSCache(tscp);
2308                 return 0;
2309             }
2310             lock_ReleaseWrite(&tscp->rw);
2311         }       /* got lock */
2312         cm_ReleaseSCache(tscp);
2313     }   /* found entry */
2314
2315 #ifdef AFS_FREELANCE_CLIENT
2316     // yj: if this is a mountpoint under root.afs then we don't want it
2317     // to be bulkstat-ed, instead, we call getSCache directly and under
2318     // getSCache, it is handled specially.
2319     if  ( cm_freelanceEnabled &&
2320           tfid.cell==AFS_FAKE_ROOT_CELL_ID && 
2321           tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2322           !(tfid.vnode==0x1 && tfid.unique==0x1) )
2323     {       
2324         osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2325         return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2326     }
2327 #endif /* AFS_FREELANCE_CLIENT */
2328
2329     i = bsp->counter++;
2330     bsp->fids[i].Volume = scp->fid.volume;
2331     bsp->fids[i].Vnode = tfid.vnode;
2332     bsp->fids[i].Unique = tfid.unique;
2333     return 0;
2334 }       
2335
2336 afs_int32
2337 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2338 {
2339     afs_int32 code = 0;
2340     AFSCBFids fidStruct;
2341     AFSBulkStats statStruct;
2342     cm_conn_t *connp;
2343     AFSCBs callbackStruct;
2344     long filex;
2345     AFSVolSync volSync;
2346     cm_callbackRequest_t cbReq;
2347     long filesThisCall;
2348     long i;
2349     long j;
2350     cm_scache_t *scp;
2351     cm_fid_t tfid;
2352     struct rx_connection * rxconnp;
2353     int inlinebulk;             /* Did we use InlineBulkStatus RPC or not? */
2354
2355     memset(&volSync, 0, sizeof(volSync));
2356
2357     /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2358      * make the calls to create the entries.  Handle AFSCBMAX files at a
2359      * time.
2360      */
2361     for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2362         filesThisCall = bbp->counter - filex;
2363         if (filesThisCall > AFSCBMAX) 
2364             filesThisCall = AFSCBMAX;
2365
2366         fidStruct.AFSCBFids_len = filesThisCall;
2367         fidStruct.AFSCBFids_val = &bbp->fids[filex];
2368         statStruct.AFSBulkStats_len = filesThisCall;
2369         statStruct.AFSBulkStats_val = &bbp->stats[filex];
2370         callbackStruct.AFSCBs_len = filesThisCall;
2371         callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2372         cm_StartCallbackGrantingCall(NULL, &cbReq);
2373         osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2374
2375         /*
2376          * Whenever cm_Analyze is called for a RXAFS_ RPC there must
2377          * be a FID provided.  However, the error code from RXAFS_BulkStatus
2378          * or RXAFS_InlinkBulkStatus does not apply to any FID.  Therefore,
2379          * we generate an invalid FID to match with the RPC error.
2380          */
2381         cm_SetFid(&tfid, dscp->fid.cell, dscp->fid.volume, 0, 0);
2382
2383         do {
2384             inlinebulk = 0;
2385
2386             code = cm_ConnFromFID(&tfid, userp, reqp, &connp);
2387             if (code) 
2388                 continue;
2389
2390             rxconnp = cm_GetRxConn(connp);
2391             if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2392                 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2393                                               &statStruct, &callbackStruct, &volSync);
2394                 if (code == RXGEN_OPCODE) {
2395                     cm_SetServerNoInlineBulk(connp->serverp, 0);
2396                 } else {
2397                     inlinebulk = 1;
2398                 }
2399             }
2400             if (!inlinebulk) {
2401                 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2402                                         &statStruct, &callbackStruct, &volSync);
2403             }
2404             rx_PutConnection(rxconnp);
2405
2406             /*
2407              * If InlineBulk RPC was called and it succeeded,
2408              * then pull out the return code from the status info
2409              * and use it for cm_Analyze so that we can failover to other
2410              * .readonly volume instances.  But only do it for errors that
2411              * are volume global.
2412              */
2413             if (inlinebulk && code == 0 && (&bbp->stats[0])->errorCode) {
2414                 osi_Log1(afsd_logp, "cm_TryBulkStat inline-bulk stat error: %d",
2415                           (&bbp->stats[0])->errorCode);
2416                 switch ((&bbp->stats[0])->errorCode) {
2417                 case VBUSY:
2418                 case VRESTARTING:
2419                 case VNOVOL:
2420                 case VMOVED:
2421                 case VOFFLINE:
2422                 case VSALVAGE:
2423                 case VNOSERVICE:
2424                 case VIO:
2425                     code = (&bbp->stats[0])->errorCode;
2426                     break;
2427                 default:
2428                     /* Rx and Rxkad errors are volume global */
2429                     if ( (&bbp->stats[0])->errorCode >= -64 && (&bbp->stats[0])->errorCode < 0 ||
2430                          (&bbp->stats[0])->errorCode >= ERROR_TABLE_BASE_RXK && (&bbp->stats[0])->errorCode < ERROR_TABLE_BASE_RXK + 256)
2431                         code = (&bbp->stats[0])->errorCode;
2432                 }
2433             }
2434         } while (cm_Analyze(connp, userp, reqp, &tfid, &volSync, NULL, &cbReq, code));
2435         code = cm_MapRPCError(code, reqp);
2436
2437         /*
2438          * might as well quit on an error, since we're not going to do
2439          * much better on the next immediate call, either.
2440          */
2441         if (code) {
2442             osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2443                       inlinebulk ? "Inline" : "", code);
2444             cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2445             break;
2446         }
2447
2448         /*
2449          * The bulk RPC has succeeded or at least not failed with a
2450          * volume global error result.  For items that have inlineBulk
2451          * errors we must call cm_Analyze in order to perform required
2452          * logging of errors.
2453          *
2454          * If the RPC was not inline bulk or the entry either has no error
2455          * the status must be merged.
2456          */
2457         osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2458
2459         for (i = 0; i<filesThisCall; i++) {
2460             j = filex + i;
2461             cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2462
2463             if (inlinebulk && (&bbp->stats[j])->errorCode) {
2464                 cm_req_t treq = *reqp;
2465                 cm_Analyze(NULL, userp, &treq, &tfid, &volSync, NULL, &cbReq, (&bbp->stats[j])->errorCode);
2466             } else {
2467                 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2468                 if (code != 0)
2469                     continue;
2470
2471                 /*
2472                  * otherwise, if this entry has no callback info,
2473                  * merge in this.  If there is existing callback info
2474                  * we skip the merge because the existing data must be
2475                  * current (we have a callback) and the response from
2476                  * a non-inline bulk rpc might actually be wrong.
2477                  *
2478                  * now, we have to be extra paranoid on merging in this
2479                  * information, since we didn't use cm_SyncOp before
2480                  * starting the fetch to make sure that no bad races
2481                  * were occurring.  Specifically, we need to make sure
2482                  * we don't obliterate any newer information in the
2483                  * vnode than have here.
2484                  *
2485                  * Right now, be pretty conservative: if there's a
2486                  * callback or a pending call, skip it.
2487                  * However, if the prior attempt to obtain status
2488                  * was refused access or the volume is .readonly,
2489                  * take the data in any case since we have nothing
2490                  * better for the in flight directory enumeration that
2491                  * resulted in this function being called.
2492                  */
2493                 lock_ObtainRead(&scp->rw);
2494                 if ((scp->cbServerp == NULL &&
2495                      !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2496                      (scp->flags & CM_SCACHEFLAG_PURERO) ||
2497                      (scp->flags & CM_SCACHEFLAG_EACCESS))
2498                 {
2499                     lock_ConvertRToW(&scp->rw);
2500                     cm_EndCallbackGrantingCall(scp, &cbReq,
2501                                                &bbp->callbacks[j],
2502                                                &volSync,
2503                                                CM_CALLBACK_MAINTAINCOUNT);
2504                     cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2505                     lock_ReleaseWrite(&scp->rw);
2506                 } else {
2507                     lock_ReleaseRead(&scp->rw);
2508                 }
2509                 cm_ReleaseSCache(scp);
2510             }
2511         } /* all files in the response */
2512         /* now tell it to drop the count,
2513          * after doing the vnode processing above */
2514         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2515     }   /* while there are still more files to process */
2516
2517     return code;
2518 }
2519
2520 /* called with a write locked scp and a pointer to a buffer.  Make bulk stat
2521  * calls on all undeleted files in the page of the directory specified.
2522  */
2523 afs_int32
2524 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2525                cm_req_t *reqp)
2526 {
2527     afs_int32 code;
2528     cm_bulkStat_t *bbp;
2529
2530     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2531
2532     /* should be on a buffer boundary */
2533     osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2534
2535     bbp = malloc(sizeof(cm_bulkStat_t));
2536     memset(bbp, 0, sizeof(cm_bulkStat_t));
2537     bbp->bufOffset = *offsetp;
2538
2539     lock_ReleaseWrite(&dscp->rw);
2540     /* first, assemble the file IDs we need to stat */
2541     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2542
2543     /* if we failed, bail out early */
2544     if (code && code != CM_ERROR_STOPNOW) {
2545         free(bbp);
2546         lock_ObtainWrite(&dscp->rw);
2547         return code;
2548     }
2549
2550     code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2551     osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2552
2553     lock_ObtainWrite(&dscp->rw);
2554     free(bbp);
2555     return 0;
2556 }       
2557
2558 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2559 {
2560     long mask;
2561
2562     /* initialize store back mask as inexpensive local variable */
2563     mask = 0;
2564     memset(statusp, 0, sizeof(AFSStoreStatus));
2565
2566     /* copy out queued info from scache first, if scp passed in */
2567     if (scp) {
2568         if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2569             statusp->ClientModTime = scp->clientModTime;
2570             mask |= AFS_SETMODTIME;
2571             scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2572         }
2573     }
2574
2575     if (attrp) {
2576         /* now add in our locally generated request */
2577         if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2578             statusp->ClientModTime = attrp->clientModTime;
2579             mask |= AFS_SETMODTIME;
2580         }
2581         if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2582             statusp->UnixModeBits = attrp->unixModeBits;
2583             mask |= AFS_SETMODE;
2584         }
2585         if (attrp->mask & CM_ATTRMASK_OWNER) {
2586             statusp->Owner = attrp->owner;
2587             mask |= AFS_SETOWNER;
2588         }
2589         if (attrp->mask & CM_ATTRMASK_GROUP) {
2590             statusp->Group = attrp->group;
2591             mask |= AFS_SETGROUP;
2592         }
2593     }
2594     statusp->Mask = mask;
2595 }       
2596
2597 /* set the file size, and make sure that all relevant buffers have been
2598  * truncated.  Ensure that any partially truncated buffers have been zeroed
2599  * to the end of the buffer.
2600  */
2601 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2602                    cm_req_t *reqp)
2603 {
2604     long code;
2605     int shrinking;
2606
2607     /* start by locking out buffer creation */
2608     lock_ObtainWrite(&scp->bufCreateLock);
2609
2610     /* verify that this is a file, not a dir or a symlink */
2611     lock_ObtainWrite(&scp->rw);
2612     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2613                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2614     if (code) 
2615         goto done;
2616     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2617
2618     if (scp->fileType != CM_SCACHETYPE_FILE) {
2619         code = CM_ERROR_ISDIR;
2620         goto done;
2621     }
2622
2623   startover:
2624     if (LargeIntegerLessThan(*sizep, scp->length))
2625         shrinking = 1;
2626     else
2627         shrinking = 0;
2628
2629     lock_ReleaseWrite(&scp->rw);
2630
2631     /* can't hold scp->rw lock here, since we may wait for a storeback to
2632      * finish if the buffer package is cleaning a buffer by storing it to
2633      * the server.
2634      */
2635     if (shrinking)
2636         buf_Truncate(scp, userp, reqp, sizep);
2637
2638     /* now ensure that file length is short enough, and update truncPos */
2639     lock_ObtainWrite(&scp->rw);
2640
2641     /* make sure we have a callback (so we have the right value for the
2642      * length), and wait for it to be safe to do a truncate.
2643      */
2644     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2645                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2646                       | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2647
2648     /* If we only have 'i' bits, then we should still be able to set
2649        the size of a file we created. */
2650     if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2651         code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2652                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2653                          | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2654     }
2655
2656     if (code) 
2657         goto done;
2658
2659     if (LargeIntegerLessThan(*sizep, scp->length)) {
2660         /* a real truncation.  If truncPos is not set yet, or is bigger
2661          * than where we're truncating the file, set truncPos to this
2662          * new value.
2663          */
2664         if (!shrinking)
2665             goto startover;
2666         if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2667              || LargeIntegerLessThan(*sizep, scp->length)) {
2668             /* set trunc pos */
2669             scp->truncPos = *sizep;
2670             scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2671         }
2672         /* in either case, the new file size has been changed */
2673         scp->length = *sizep;
2674         scp->mask |= CM_SCACHEMASK_LENGTH;
2675     }
2676     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2677         /* really extending the file */
2678         scp->length = *sizep;
2679         scp->mask |= CM_SCACHEMASK_LENGTH;
2680     }
2681
2682     /* done successfully */
2683     code = 0;
2684
2685     cm_SyncOpDone(scp, NULL, 
2686                    CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2687                    | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2688
2689   done:
2690     lock_ReleaseWrite(&scp->rw);
2691     lock_ReleaseWrite(&scp->bufCreateLock);
2692
2693     return code;
2694 }
2695
2696 /* set the file size or other attributes (but not both at once) */
2697 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2698                 cm_req_t *reqp)
2699 {
2700     long code;
2701     AFSFetchStatus afsOutStatus;
2702     AFSVolSync volSync;
2703     cm_conn_t *connp;
2704     AFSFid tfid;
2705     AFSStoreStatus afsInStatus;
2706     struct rx_connection * rxconnp;
2707
2708     memset(&volSync, 0, sizeof(volSync));
2709
2710     /* handle file length setting */
2711     if (attrp->mask & CM_ATTRMASK_LENGTH)
2712         return cm_SetLength(scp, &attrp->length, userp, reqp);
2713
2714     lock_ObtainWrite(&scp->rw);
2715     /* otherwise, we have to make an RPC to get the status */
2716     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2717     if (code) {
2718         lock_ReleaseWrite(&scp->rw);
2719         return code;
2720     }
2721     lock_ConvertWToR(&scp->rw);
2722
2723     /* make the attr structure */
2724     cm_StatusFromAttr(&afsInStatus, scp, attrp);
2725
2726     tfid.Volume = scp->fid.volume;
2727     tfid.Vnode = scp->fid.vnode;
2728     tfid.Unique = scp->fid.unique;
2729     lock_ReleaseRead(&scp->rw);
2730
2731     /* now make the RPC */
2732     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2733     do {
2734         code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2735         if (code) 
2736             continue;
2737
2738         rxconnp = cm_GetRxConn(connp);
2739         code = RXAFS_StoreStatus(rxconnp, &tfid,
2740                                   &afsInStatus, &afsOutStatus, &volSync);
2741         rx_PutConnection(rxconnp);
2742
2743     } while (cm_Analyze(connp, userp, reqp,
2744                          &scp->fid, &volSync, NULL, NULL, code));
2745     code = cm_MapRPCError(code, reqp);
2746
2747     if (code)
2748         osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2749     else
2750         osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2751
2752     lock_ObtainWrite(&scp->rw);
2753     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2754     if (code == 0)
2755         cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2756                         CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2757         
2758     /* if we're changing the mode bits, discard the ACL cache, 
2759      * since we changed the mode bits.
2760      */
2761     if (afsInStatus.Mask & AFS_SETMODE) 
2762         cm_FreeAllACLEnts(scp);
2763     lock_ReleaseWrite(&scp->rw);
2764     return code;
2765 }       
2766
2767 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2768                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2769 {       
2770     cm_conn_t *connp;
2771     long code;
2772     AFSFid dirAFSFid;
2773     cm_callbackRequest_t cbReq;
2774     AFSFid newAFSFid;
2775     cm_fid_t newFid;
2776     cm_scache_t *scp = NULL;
2777     int didEnd;
2778     AFSStoreStatus inStatus;
2779     AFSFetchStatus updatedDirStatus;
2780     AFSFetchStatus newFileStatus;
2781     AFSCallBack newFileCallback;
2782     AFSVolSync volSync;
2783     struct rx_connection * rxconnp;
2784     cm_dirOp_t dirop;
2785     fschar_t * fnamep = NULL;
2786
2787     memset(&volSync, 0, sizeof(volSync));
2788
2789     /* can't create names with @sys in them; must expand it manually first.
2790      * return "invalid request" if they try.
2791      */
2792     if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2793         return CM_ERROR_ATSYS;
2794     }
2795
2796 #ifdef AFS_FREELANCE_CLIENT
2797     /* Freelance root volume does not hold files */
2798     if (cm_freelanceEnabled &&
2799         dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2800         dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2801     {
2802         return CM_ERROR_NOACCESS;
2803     }
2804 #endif /* AFS_FREELANCE_CLIENT */
2805
2806     /* before starting the RPC, mark that we're changing the file data, so
2807      * that someone who does a chmod will know to wait until our call
2808      * completes.
2809      */
2810     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2811                   &dirop);
2812     lock_ObtainWrite(&dscp->rw);
2813     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2814     lock_ReleaseWrite(&dscp->rw);
2815     if (code == 0) {
2816         cm_StartCallbackGrantingCall(NULL, &cbReq);
2817     } else {
2818         cm_EndDirOp(&dirop);
2819     }
2820     if (code) {
2821         return code;
2822     }
2823     didEnd = 0;
2824
2825     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2826
2827     cm_StatusFromAttr(&inStatus, NULL, attrp);
2828
2829     /* try the RPC now */
2830     osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2831     do {
2832         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2833         if (code) 
2834             continue;
2835
2836         dirAFSFid.Volume = dscp->fid.volume;
2837         dirAFSFid.Vnode = dscp->fid.vnode;
2838         dirAFSFid.Unique = dscp->fid.unique;
2839
2840         rxconnp = cm_GetRxConn(connp);
2841         code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2842                                  &inStatus, &newAFSFid, &newFileStatus,
2843                                  &updatedDirStatus, &newFileCallback,
2844                                  &volSync);
2845         rx_PutConnection(rxconnp);
2846
2847     } while (cm_Analyze(connp, userp, reqp,
2848                          &dscp->fid, &volSync, NULL, &cbReq, code));
2849     code = cm_MapRPCError(code, reqp);
2850         
2851     if (code)
2852         osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2853     else
2854         osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2855
2856     if (dirop.scp) {
2857         lock_ObtainWrite(&dirop.scp->dirlock);
2858         dirop.lockType = CM_DIRLOCK_WRITE;
2859     }
2860     lock_ObtainWrite(&dscp->rw);
2861     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2862     if (code == 0) {
2863         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2864     }
2865     lock_ReleaseWrite(&dscp->rw);
2866
2867     /* now try to create the file's entry, too, but be careful to 
2868      * make sure that we don't merge in old info.  Since we weren't locking
2869      * out any requests during the file's creation, we may have pretty old
2870      * info.
2871      */
2872     if (code == 0) {
2873         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2874         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2875         if (code == 0) {
2876             lock_ObtainWrite(&scp->rw);
2877             scp->creator = userp;               /* remember who created it */
2878             if (!cm_HaveCallback(scp)) {
2879                 cm_EndCallbackGrantingCall(scp, &cbReq,
2880                                            &newFileCallback, &volSync, 0);
2881                 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2882                                userp, reqp, 0);
2883                 didEnd = 1;     
2884             }       
2885             lock_ReleaseWrite(&scp->rw);
2886         }
2887     }
2888
2889     /* make sure we end things properly */
2890     if (!didEnd)
2891         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2892
2893     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2894         cm_DirCreateEntry(&dirop, fnamep, &newFid);
2895 #ifdef USE_BPLUS
2896         cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2897 #endif
2898     }
2899     cm_EndDirOp(&dirop);
2900
2901     if (fnamep)
2902         free(fnamep);
2903
2904     if (scp) {
2905         if (scpp)
2906             *scpp = scp;
2907         else
2908             cm_ReleaseSCache(scp);
2909     }
2910     return code;
2911 }       
2912
2913 /*
2914  * locked if TRUE means write-locked
2915  * else the cm_scache_t rw must not be held
2916  */
2917 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
2918 {
2919     long code;
2920
2921     if (locked)
2922         lock_ReleaseWrite(&scp->rw);
2923     code = buf_CleanVnode(scp, userp, reqp);
2924     if (code == 0) {
2925         lock_ObtainWrite(&scp->rw);
2926
2927         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2928                           | CM_SCACHEMASK_CLIENTMODTIME
2929                           | CM_SCACHEMASK_LENGTH))
2930             code = cm_StoreMini(scp, userp, reqp);
2931
2932         if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2933             code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2934             scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2935         }
2936
2937         if (!locked)
2938             lock_ReleaseWrite(&scp->rw);
2939     } else if (locked) {
2940         lock_ObtainWrite(&scp->rw);
2941     }
2942     return code;
2943 }
2944
2945 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2946                 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2947 {
2948     cm_conn_t *connp;
2949     long code;
2950     AFSFid dirAFSFid;
2951     cm_callbackRequest_t cbReq;
2952     AFSFid newAFSFid;
2953     cm_fid_t newFid;
2954     cm_scache_t *scp = NULL;
2955     int didEnd;
2956     AFSStoreStatus inStatus;
2957     AFSFetchStatus updatedDirStatus;
2958     AFSFetchStatus newDirStatus;
2959     AFSCallBack newDirCallback;
2960     AFSVolSync volSync;
2961     struct rx_connection * rxconnp;
2962     cm_dirOp_t dirop;
2963     fschar_t * fnamep = NULL;
2964
2965     memset(&volSync, 0, sizeof(volSync));
2966
2967     /* can't create names with @sys in them; must expand it manually first.
2968      * return "invalid request" if they try.
2969      */
2970     if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2971         return CM_ERROR_ATSYS;
2972     }
2973
2974 #ifdef AFS_FREELANCE_CLIENT
2975     /* Freelance root volume does not hold subdirectories */
2976     if (cm_freelanceEnabled &&
2977         dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2978         dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2979     {
2980         return CM_ERROR_NOACCESS;
2981     }
2982 #endif /* AFS_FREELANCE_CLIENT */
2983
2984     /* before starting the RPC, mark that we're changing the directory
2985      * data, so that someone who does a chmod on the dir will wait until
2986      * our call completes.
2987      */
2988     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2989                   &dirop);
2990     lock_ObtainWrite(&dscp->rw);
2991     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2992     lock_ReleaseWrite(&dscp->rw);
2993     if (code == 0) {
2994         cm_StartCallbackGrantingCall(NULL, &cbReq);
2995     } else {
2996         cm_EndDirOp(&dirop);
2997     }
2998     if (code) {
2999         return code;
3000     }
3001     didEnd = 0;
3002
3003     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3004     cm_StatusFromAttr(&inStatus, NULL, attrp);
3005
3006     /* try the RPC now */
3007     osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3008     do {
3009         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3010         if (code) 
3011             continue;
3012
3013         dirAFSFid.Volume = dscp->fid.volume;
3014         dirAFSFid.Vnode = dscp->fid.vnode;
3015         dirAFSFid.Unique = dscp->fid.unique;
3016
3017         rxconnp = cm_GetRxConn(connp);
3018         code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3019                               &inStatus, &newAFSFid, &newDirStatus,
3020                               &updatedDirStatus, &newDirCallback,
3021                               &volSync);
3022         rx_PutConnection(rxconnp);
3023
3024     } while (cm_Analyze(connp, userp, reqp,
3025                         &dscp->fid, &volSync, NULL, &cbReq, code));
3026     code = cm_MapRPCError(code, reqp);
3027         
3028     if (code)
3029         osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3030     else
3031         osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3032
3033     if (dirop.scp) {
3034         lock_ObtainWrite(&dirop.scp->dirlock);
3035         dirop.lockType = CM_DIRLOCK_WRITE;
3036     }
3037     lock_ObtainWrite(&dscp->rw);
3038     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3039     if (code == 0) {
3040         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3041     }
3042     lock_ReleaseWrite(&dscp->rw);
3043
3044     /* now try to create the new dir's entry, too, but be careful to 
3045      * make sure that we don't merge in old info.  Since we weren't locking
3046      * out any requests during the file's creation, we may have pretty old
3047      * info.
3048      */
3049     if (code == 0) {
3050         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3051         code = cm_GetSCache(&newFid, &scp, userp, reqp);
3052         if (code == 0) {
3053             lock_ObtainWrite(&scp->rw);
3054             if (!cm_HaveCallback(scp)) {
3055                 cm_EndCallbackGrantingCall(scp, &cbReq,
3056                                             &newDirCallback, &volSync, 0);
3057                 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
3058                                 userp, reqp, 0);
3059                 didEnd = 1;             
3060             }
3061             lock_ReleaseWrite(&scp->rw);
3062         }
3063     }
3064
3065     /* make sure we end things properly */
3066     if (!didEnd)
3067         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3068
3069     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
3070         cm_DirCreateEntry(&dirop, fnamep, &newFid);
3071 #ifdef USE_BPLUS
3072         cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3073 #endif
3074     }
3075     cm_EndDirOp(&dirop);
3076
3077     free(fnamep);
3078
3079     if (scp) {
3080         if (scpp)
3081             *scpp = scp;
3082         else
3083             cm_ReleaseSCache(scp);
3084     }
3085
3086     /* and return error code */
3087     return code;
3088 }       
3089
3090 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3091              cm_user_t *userp, cm_req_t *reqp)
3092 {
3093     cm_conn_t *connp;
3094     long code = 0;
3095     AFSFid dirAFSFid;
3096     AFSFid existingAFSFid;
3097     AFSFetchStatus updatedDirStatus;
3098     AFSFetchStatus newLinkStatus;
3099     AFSVolSync volSync;
3100     struct rx_connection * rxconnp;
3101     cm_dirOp_t dirop;
3102     fschar_t * fnamep = NULL;
3103
3104     memset(&volSync, 0, sizeof(volSync));
3105
3106     if (dscp->fid.cell != sscp->fid.cell ||
3107         dscp->fid.volume != sscp->fid.volume) {
3108         return CM_ERROR_CROSSDEVLINK;
3109     }
3110
3111     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3112                   &dirop);
3113     lock_ObtainWrite(&dscp->rw);
3114     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3115     lock_ReleaseWrite(&dscp->rw);
3116     if (code != 0)
3117         cm_EndDirOp(&dirop);
3118
3119     if (code)
3120         return code;
3121
3122     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3123
3124     /* try the RPC now */
3125     osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3126     do {
3127         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3128         if (code) continue;
3129
3130         dirAFSFid.Volume = dscp->fid.volume;
3131         dirAFSFid.Vnode = dscp->fid.vnode;
3132         dirAFSFid.Unique = dscp->fid.unique;
3133
3134         existingAFSFid.Volume = sscp->fid.volume;
3135         existingAFSFid.Vnode = sscp->fid.vnode;
3136         existingAFSFid.Unique = sscp->fid.unique;
3137
3138         rxconnp = cm_GetRxConn(connp);
3139         code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3140             &newLinkStatus, &updatedDirStatus, &volSync);
3141         rx_PutConnection(rxconnp);
3142         osi_Log1(afsd_logp,"  RXAFS_Link returns 0x%x", code);
3143
3144     } while (cm_Analyze(connp, userp, reqp,
3145         &dscp->fid, &volSync, NULL, NULL, code));
3146
3147     code = cm_MapRPCError(code, reqp);
3148
3149     if (code)
3150         osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3151     else
3152         osi_Log0(afsd_logp, "CALL Link SUCCESS");
3153
3154     if (dirop.scp) {
3155         lock_ObtainWrite(&dirop.scp->dirlock);
3156         dirop.lockType = CM_DIRLOCK_WRITE;
3157     }
3158     lock_ObtainWrite(&dscp->rw);
3159     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3160     if (code == 0) {
3161         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3162     }
3163     lock_ReleaseWrite(&dscp->rw);
3164
3165     if (code == 0) {
3166         if (cm_CheckDirOpForSingleChange(&dirop)) {
3167             cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3168 #ifdef USE_BPLUS
3169             cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3170 #endif
3171         }
3172     }
3173     cm_EndDirOp(&dirop);
3174
3175     /* Update the linked object status */
3176     if (code == 0) {
3177         lock_ObtainWrite(&sscp->rw);
3178         cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3179         lock_ReleaseWrite(&sscp->rw);
3180     }
3181
3182     free(fnamep);
3183
3184     return code;
3185 }
3186
3187 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3188                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3189 {
3190     cm_conn_t *connp;
3191     long code;
3192     AFSFid dirAFSFid;
3193     AFSFid newAFSFid;
3194     cm_fid_t newFid;
3195     cm_scache_t *scp;
3196     AFSStoreStatus inStatus;
3197     AFSFetchStatus updatedDirStatus;
3198     AFSFetchStatus newLinkStatus;
3199     AFSVolSync volSync;
3200     struct rx_connection * rxconnp;
3201     cm_dirOp_t dirop;
3202     fschar_t *fnamep = NULL;
3203
3204     memset(&volSync, 0, sizeof(volSync));
3205
3206     /* before starting the RPC, mark that we're changing the directory data,
3207      * so that someone who does a chmod on the dir will wait until our
3208      * call completes.
3209      */
3210     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3211                   &dirop);
3212     lock_ObtainWrite(&dscp->rw);
3213     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3214     lock_ReleaseWrite(&dscp->rw);
3215     if (code != 0)
3216         cm_EndDirOp(&dirop);
3217     if (code) {
3218         return code;
3219     }
3220
3221     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3222
3223     cm_StatusFromAttr(&inStatus, NULL, attrp);
3224
3225     /* try the RPC now */
3226     osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3227     do {
3228         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3229         if (code) 
3230             continue;
3231
3232         dirAFSFid.Volume = dscp->fid.volume;
3233         dirAFSFid.Vnode = dscp->fid.vnode;
3234         dirAFSFid.Unique = dscp->fid.unique;
3235
3236         rxconnp = cm_GetRxConn(connp);
3237         code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3238                               &inStatus, &newAFSFid, &newLinkStatus,
3239                               &updatedDirStatus, &volSync);
3240         rx_PutConnection(rxconnp);
3241
3242     } while (cm_Analyze(connp, userp, reqp,
3243                          &dscp->fid, &volSync, NULL, NULL, code));
3244     code = cm_MapRPCError(code, reqp);
3245         
3246     if (code)
3247         osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3248     else
3249         osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3250
3251     if (dirop.scp) {
3252         lock_ObtainWrite(&dirop.scp->dirlock);
3253         dirop.lockType = CM_DIRLOCK_WRITE;
3254     }
3255     lock_ObtainWrite(&dscp->rw);
3256     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3257     if (code == 0) {
3258         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3259     }
3260     lock_ReleaseWrite(&dscp->rw);
3261
3262     if (code == 0) {
3263         if (cm_CheckDirOpForSingleChange(&dirop)) {
3264             cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3265
3266             cm_DirCreateEntry(&dirop, fnamep, &newFid);
3267 #ifdef USE_BPLUS
3268             cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3269 #endif
3270         }
3271     }
3272     cm_EndDirOp(&dirop);
3273
3274     /* now try to create the new dir's entry, too, but be careful to 
3275      * make sure that we don't merge in old info.  Since we weren't locking
3276      * out any requests during the file's creation, we may have pretty old
3277      * info.
3278      */
3279     if (code == 0) {
3280         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3281         code = cm_GetSCache(&newFid, &scp, userp, reqp);
3282         if (code == 0) {
3283             lock_ObtainWrite(&scp->rw);
3284             if (!cm_HaveCallback(scp)) {
3285                 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3286                                 userp, reqp, 0);
3287             }       
3288             lock_ReleaseWrite(&scp->rw);
3289             cm_ReleaseSCache(scp);
3290         }
3291     }
3292
3293     free(fnamep);
3294         
3295     /* and return error code */
3296     return code;
3297 }
3298
3299 /*! \brief Remove a directory
3300
3301   Encapsulates a call to RXAFS_RemoveDir().
3302
3303   \param[in] dscp cm_scache_t for the directory containing the
3304       directory to be removed.
3305
3306   \param[in] fnamep This will be the original name of the directory
3307       as known to the file server.   It will be passed in to RXAFS_RemoveDir().
3308       This parameter is optional.  If it is not provided the value
3309       will be looked up.
3310
3311   \param[in] cnamep Normalized name used to update the local
3312       directory caches.
3313
3314   \param[in] userp cm_user_t for the request.
3315
3316   \param[in] reqp Request tracker.
3317 */
3318 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3319 {
3320     cm_conn_t *connp;
3321     long code;
3322     AFSFid dirAFSFid;
3323     int didEnd;
3324     AFSFetchStatus updatedDirStatus;
3325     AFSVolSync volSync;
3326     struct rx_connection * rxconnp;
3327     cm_dirOp_t dirop;
3328     cm_scache_t *scp = NULL;
3329     int free_fnamep = FALSE;
3330
3331     memset(&volSync, 0, sizeof(volSync));
3332
3333     if (fnamep == NULL) {
3334         code = -1;
3335 #ifdef USE_BPLUS
3336         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
3337                              CM_DIROP_FLAG_NONE, &dirop);
3338         if (code == 0) {
3339             code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3340             if (code == 0)
3341                 free_fnamep = TRUE;
3342             cm_EndDirOp(&dirop);
3343         }
3344 #endif
3345         if (code)
3346             goto done;
3347     }
3348
3349     code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3350     if (code)
3351         goto done;
3352
3353     /* before starting the RPC, mark that we're changing the directory data,
3354      * so that someone who does a chmod on the dir will wait until our
3355      * call completes.
3356      */
3357     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3358                   &dirop);
3359     lock_ObtainWrite(&dscp->rw);
3360     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3361     lock_ReleaseWrite(&dscp->rw);
3362     if (code) {
3363         cm_EndDirOp(&dirop);
3364         goto done;
3365     }
3366     didEnd = 0;
3367
3368     /* try the RPC now */
3369     osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3370     do {
3371         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3372         if (code) 
3373             continue;
3374
3375         dirAFSFid.Volume = dscp->fid.volume;
3376         dirAFSFid.Vnode = dscp->fid.vnode;
3377         dirAFSFid.Unique = dscp->fid.unique;
3378
3379         rxconnp = cm_GetRxConn(connp);
3380         code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3381                                &updatedDirStatus, &volSync);
3382         rx_PutConnection(rxconnp);
3383
3384     } while (cm_Analyze(connp, userp, reqp,
3385                         &dscp->fid, &volSync, NULL, NULL, code));
3386     code = cm_MapRPCErrorRmdir(code, reqp);
3387
3388     if (code)
3389         osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3390     else
3391         osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3392
3393     if (dirop.scp) {
3394         lock_ObtainWrite(&dirop.scp->dirlock);
3395         dirop.lockType = CM_DIRLOCK_WRITE;
3396     }
3397     lock_ObtainWrite(&dscp->rw);
3398     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3399     if (code == 0) {
3400         cm_dnlcRemove(dscp, cnamep); 
3401         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3402     }
3403     lock_ReleaseWrite(&dscp->rw);
3404
3405     if (code == 0) {
3406         if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3407             cm_DirDeleteEntry(&dirop, fnamep);
3408 #ifdef USE_BPLUS
3409             cm_BPlusDirDeleteEntry(&dirop, cnamep);
3410 #endif
3411         }
3412     }
3413     cm_EndDirOp(&dirop);
3414
3415     if (scp) {
3416         cm_ReleaseSCache(scp);
3417         if (code == 0) {
3418             lock_ObtainWrite(&scp->rw);
3419             scp->flags |= CM_SCACHEFLAG_DELETED;
3420             lock_ReleaseWrite(&scp->rw);
3421         }
3422     }
3423
3424   done:
3425     if (free_fnamep)
3426         free(fnamep);
3427
3428     /* and return error code */
3429     return code;
3430 }
3431
3432 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3433 {
3434     /* grab mutex on contents */
3435     lock_ObtainWrite(&scp->rw);
3436
3437     /* reset the prefetch info */
3438     scp->prefetch.base.LowPart = 0;             /* base */
3439     scp->prefetch.base.HighPart = 0;
3440     scp->prefetch.end.LowPart = 0;              /* and end */
3441     scp->prefetch.end.HighPart = 0;
3442
3443     /* release mutex on contents */
3444     lock_ReleaseWrite(&scp->rw);
3445
3446     /* we're done */
3447     return 0;
3448 }       
3449
3450 /*! \brief Rename a file or directory
3451
3452   Encapsulates a RXAFS_Rename() call.
3453
3454   \param[in] oldDscp cm_scache_t for the directory containing the old
3455       name.
3456
3457   \param[in] oldNamep The original old name known to the file server.
3458       This is the name that will be passed into the RXAFS_Rename().
3459       If it is not provided, it will be looked up.
3460
3461   \param[in] normalizedOldNamep Normalized old name.  This is used for
3462   updating local directory caches.
3463
3464   \param[in] newDscp cm_scache_t for the directory containing the new
3465   name.
3466
3467   \param[in] newNamep New name. Normalized.
3468
3469   \param[in] userp cm_user_t for the request.
3470
3471   \param[in,out] reqp Request tracker.
3472
3473 */
3474 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3475                cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3476                cm_req_t *reqp)
3477 {
3478     cm_conn_t *connp;
3479     long code;
3480     AFSFid oldDirAFSFid;
3481     AFSFid newDirAFSFid;
3482     int didEnd;
3483     AFSFetchStatus updatedOldDirStatus;
3484     AFSFetchStatus updatedNewDirStatus;
3485     AFSVolSync volSync;
3486     int oneDir;
3487     struct rx_connection * rxconnp;
3488     cm_dirOp_t oldDirOp;
3489     cm_fid_t   fileFid;
3490     int        diropCode = -1;
3491     cm_dirOp_t newDirOp;
3492     fschar_t * newNamep = NULL;
3493     int free_oldNamep = FALSE;
3494     cm_scache_t *oldScp = NULL, *newScp = NULL;
3495
3496     memset(&volSync, 0, sizeof(volSync));
3497
3498     if (cOldNamep == NULL || cNewNamep == NULL ||
3499         cm_ClientStrLen(cOldNamep) == 0 ||
3500         cm_ClientStrLen(cNewNamep) == 0)
3501         return CM_ERROR_INVAL;
3502
3503     /* 
3504      * Before we permit the operation, make sure that we do not already have
3505      * an object in the destination directory that has a case-insensitive match
3506      * for this name UNLESS the matching object is the object we are renaming.
3507      */
3508     code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3509     if (code) {
3510         osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed", 
3511                  oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3512         goto done;
3513     }
3514
3515     code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3516     if (code == 0) {
3517         /* found a matching object with the new name */
3518         if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3519             /* and they don't match so return an error */
3520             osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists", 
3521                       newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3522             code = CM_ERROR_EXISTS;
3523         }
3524         cm_ReleaseSCache(newScp);
3525         newScp = NULL;
3526     } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3527         code = CM_ERROR_EXISTS;
3528     } else {
3529         code = 0;
3530     }
3531     if (code) 
3532         goto done;
3533
3534     if (oldNamep == NULL) {
3535         code = -1;
3536 #ifdef USE_BPLUS
3537         code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ,
3538                              CM_DIROP_FLAG_NONE, &oldDirOp);
3539         if (code == 0) {
3540             code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3541             if (code == 0)
3542                 free_oldNamep = TRUE;
3543             cm_EndDirOp(&oldDirOp);
3544         }
3545 #endif
3546         if (code) {
3547             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed", 
3548                       oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3549             goto done;
3550         }
3551     }
3552
3553
3554     /* before starting the RPC, mark that we're changing the directory data,
3555      * so that someone who does a chmod on the dir will wait until our call
3556      * completes.  We do this in vnode order so that we don't deadlock,
3557      * which makes the code a little verbose.
3558      */
3559     if (oldDscp == newDscp) {
3560         /* check for identical names */
3561         if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3562             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL", 
3563                       oldDscp, newDscp);
3564             code = CM_ERROR_RENAME_IDENTICAL;
3565             goto done;
3566         }
3567
3568         oneDir = 1;
3569         cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3570                       CM_DIROP_FLAG_NONE, &oldDirOp);
3571         lock_ObtainWrite(&oldDscp->rw);
3572         cm_dnlcRemove(oldDscp, cOldNamep);
3573         cm_dnlcRemove(oldDscp, cNewNamep);
3574         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3575                           CM_SCACHESYNC_STOREDATA);
3576         lock_ReleaseWrite(&oldDscp->rw);
3577         if (code != 0) {
3578             cm_EndDirOp(&oldDirOp);
3579         }
3580     }
3581     else {
3582         /* two distinct dir vnodes */
3583         oneDir = 0;
3584         if (oldDscp->fid.cell != newDscp->fid.cell ||
3585              oldDscp->fid.volume != newDscp->fid.volume) {
3586             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK", 
3587                       oldDscp, newDscp);
3588             code = CM_ERROR_CROSSDEVLINK;
3589             goto done;
3590         }
3591
3592         /* shouldn't happen that we have distinct vnodes for two
3593          * different files, but could due to deliberate attack, or
3594          * stale info.  Avoid deadlocks and quit now.
3595          */
3596         if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3597             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision", 
3598                       oldDscp, newDscp);
3599             code = CM_ERROR_CROSSDEVLINK;
3600             goto done;
3601         }
3602
3603         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3604             cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3605                           CM_DIROP_FLAG_NONE, &oldDirOp);
3606             lock_ObtainWrite(&oldDscp->rw);
3607             cm_dnlcRemove(oldDscp, cOldNamep);
3608             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3609                              CM_SCACHESYNC_STOREDATA);
3610             lock_ReleaseWrite(&oldDscp->rw);
3611             if (code != 0)
3612                 cm_EndDirOp(&oldDirOp);
3613             if (code == 0) {
3614                 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3615                               CM_DIROP_FLAG_NONE, &newDirOp);
3616                 lock_ObtainWrite(&newDscp->rw);
3617                 cm_dnlcRemove(newDscp, cNewNamep);
3618                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3619                                  CM_SCACHESYNC_STOREDATA);
3620                 lock_ReleaseWrite(&newDscp->rw);
3621                 if (code) {
3622                     cm_EndDirOp(&newDirOp);
3623
3624                     /* cleanup first one */
3625                     lock_ObtainWrite(&oldDscp->rw);
3626                     cm_SyncOpDone(oldDscp, NULL,
3627                                    CM_SCACHESYNC_STOREDATA);
3628                     lock_ReleaseWrite(&oldDscp->rw);
3629                     cm_EndDirOp(&oldDirOp);
3630                 }       
3631             }
3632         }
3633         else {
3634             /* lock the new vnode entry first */
3635             cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3636                           CM_DIROP_FLAG_NONE, &newDirOp);
3637             lock_ObtainWrite(&newDscp->rw);
3638             cm_dnlcRemove(newDscp, cNewNamep);
3639             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3640                               CM_SCACHESYNC_STOREDATA);
3641             lock_ReleaseWrite(&newDscp->rw);
3642             if (code != 0)
3643                 cm_EndDirOp(&newDirOp);
3644             if (code == 0) {
3645                 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3646                               CM_DIROP_FLAG_NONE, &oldDirOp);
3647                 lock_ObtainWrite(&oldDscp->rw);
3648                 cm_dnlcRemove(oldDscp, cOldNamep);
3649                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3650                                   CM_SCACHESYNC_STOREDATA);
3651                 lock_ReleaseWrite(&oldDscp->rw);
3652                 if (code != 0)
3653                     cm_EndDirOp(&oldDirOp);
3654                 if (code) {
3655                     /* cleanup first one */
3656                     lock_ObtainWrite(&newDscp->rw);
3657                     cm_SyncOpDone(newDscp, NULL,
3658                                    CM_SCACHESYNC_STOREDATA);
3659                     lock_ReleaseWrite(&newDscp->rw);
3660                     cm_EndDirOp(&newDirOp);
3661                 }       
3662             }
3663         }
3664     }   /* two distinct vnodes */
3665
3666     if (code) 
3667         goto done;
3668
3669     didEnd = 0;
3670
3671     newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3672
3673     /* try the RPC now */
3674     osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p", 
3675               oldDscp, newDscp);
3676     do {
3677         code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3678         if (code) 
3679             continue;
3680
3681         oldDirAFSFid.Volume = oldDscp->fid.volume;
3682         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3683         oldDirAFSFid.Unique = oldDscp->fid.unique;
3684         newDirAFSFid.Volume = newDscp->fid.volume;
3685         newDirAFSFid.Vnode = newDscp->fid.vnode;
3686         newDirAFSFid.Unique = newDscp->fid.unique;
3687
3688         rxconnp = cm_GetRxConn(connp);
3689         code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3690                             &newDirAFSFid, newNamep,
3691                             &updatedOldDirStatus, &updatedNewDirStatus,
3692                             &volSync);
3693         rx_PutConnection(rxconnp);
3694
3695     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3696                          &volSync, NULL, NULL, code));
3697     code = cm_MapRPCError(code, reqp);
3698         
3699     if (code)
3700         osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3701     else
3702         osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3703
3704     /* update the individual stat cache entries for the directories */
3705     if (oldDirOp.scp) {
3706         lock_ObtainWrite(&oldDirOp.scp->dirlock);
3707         oldDirOp.lockType = CM_DIRLOCK_WRITE;
3708     }
3709     lock_ObtainWrite(&oldDscp->rw);
3710     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3711
3712     if (code == 0)
3713         cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3714                        userp, reqp, CM_MERGEFLAG_DIROP);
3715     lock_ReleaseWrite(&oldDscp->rw);
3716
3717     if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3718 #ifdef USE_BPLUS
3719         diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3720         if (diropCode == CM_ERROR_INEXACT_MATCH)
3721             diropCode = 0;
3722         else if (diropCode == EINVAL)
3723 #endif
3724             diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3725
3726         if (diropCode == 0) {
3727             if (oneDir) {
3728                 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3729 #ifdef USE_BPLUS        
3730                 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3731 #endif
3732             }
3733                 
3734             if (diropCode == 0) { 
3735                 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3736 #ifdef USE_BPLUS
3737                 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3738 #endif  
3739             }
3740         }
3741     }
3742     cm_EndDirOp(&oldDirOp);
3743
3744     /* and update it for the new one, too, if necessary */
3745     if (!oneDir) {
3746         if (newDirOp.scp) {
3747             lock_ObtainWrite(&newDirOp.scp->dirlock);
3748             newDirOp.lockType = CM_DIRLOCK_WRITE;
3749         }
3750         lock_ObtainWrite(&newDscp->rw);
3751         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3752         if (code == 0)
3753             cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3754                             userp, reqp, CM_MERGEFLAG_DIROP);
3755         lock_ReleaseWrite(&newDscp->rw);
3756
3757 #if 0 
3758         /* 
3759          * The following optimization does not work.  
3760          * When the file server processed a RXAFS_Rename() request the 
3761          * FID of the object being moved between directories is not 
3762          * preserved.  The client does not know the new FID nor the 
3763          * version number of the target.  Not only can we not create
3764          * the directory entry in the new directory, but we can't 
3765          * preserve the cached data for the file.  It must be re-read
3766          * from the file server.  - jaltman, 2009/02/20
3767          */
3768         if (code == 0) {
3769             /* we only make the local change if we successfully made
3770                the change in the old directory AND there was only one
3771                change in the new directory */
3772             if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3773                 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3774 #ifdef USE_BPLUS
3775                 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3776 #endif
3777             }
3778         }
3779 #endif /* 0 */
3780         cm_EndDirOp(&newDirOp);
3781     }
3782
3783     /* 
3784      * After the rename the file server has invalidated the callbacks
3785      * on the file that was moved nor do we have a directory reference 
3786      * to it anymore.
3787      */
3788     lock_ObtainWrite(&oldScp->rw);
3789     cm_DiscardSCache(oldScp);
3790     lock_ReleaseWrite(&oldScp->rw);
3791
3792   done:
3793     if (oldScp)
3794         cm_ReleaseSCache(oldScp);
3795
3796     if (free_oldNamep)
3797         free(oldNamep);
3798
3799     free(newNamep);
3800
3801     /* and return error code */
3802     return code;
3803 }
3804
3805 /* Byte range locks:
3806
3807    The OpenAFS Windows client has to fake byte range locks given no
3808    server side support for such locks.  This is implemented as keyed
3809    byte range locks on the cache manager.
3810
3811    Keyed byte range locks:
3812
3813    Each cm_scache_t structure keeps track of a list of keyed locks.
3814    The key for a lock identifies an owner of a set of locks (referred
3815    to as a client).  Each key is represented by a value.  The set of
3816    key values used within a specific cm_scache_t structure form a
3817    namespace that has a scope of just that cm_scache_t structure.  The
3818    same key value can be used with another cm_scache_t structure and
3819    correspond to a completely different client.  However it is
3820    advantageous for the SMB or IFS layer to make sure that there is a
3821    1-1 mapping between client and keys over all cm_scache_t objects.
3822
3823    Assume a client C has key Key(C) (although, since the scope of the
3824    key is a cm_scache_t, the key can be Key(C,S), where S is the
3825    cm_scache_t.  But assume a 1-1 relation between keys and clients).
3826    A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3827    inclusive (a.k.a. [O,O+L-1]).  The function Key(x) is implemented
3828    through cm_generateKey() function for both SMB and IFS.
3829
3830    The list of locks for a cm_scache_t object S is maintained in
3831    S->fileLocks.  The cache manager will set a lock on the AFS file
3832    server in order to assert the locks in S->fileLocks.  If only
3833    shared locks are in place for S, then the cache manager will obtain
3834    a LockRead lock, while if there are any exclusive locks, it will
3835    obtain a LockWrite lock.  If the exclusive locks are all released
3836    while the shared locks remain, then the cache manager will
3837    downgrade the lock from LockWrite to LockRead.  Similarly, if an
3838    exclusive lock is obtained when only shared locks exist, then the
3839    cache manager will try to upgrade the lock from LockRead to
3840    LockWrite.
3841
3842    Each lock L owned by client C maintains a key L->key such that
3843    L->key == Key(C), the effective range defined by L->LOffset and
3844    L->LLength such that the range of bytes affected by the lock is
3845    (L->LOffset, +L->LLength), a type maintained in L->LockType which
3846    is either exclusive or shared.
3847
3848    Lock states:
3849
3850    A lock exists iff it is in S->fileLocks for some cm_scache_t
3851    S. Existing locks are in one of the following states: ACTIVE,
3852    WAITLOCK, WAITUNLOCK, LOST, DELETED.
3853
3854    The following sections describe each lock and the associated
3855    transitions.
3856
3857    1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3858       the lock with the AFS file server.  This type of lock can be
3859       exercised by a client to read or write to the locked region (as
3860       the lock allows).
3861
3862       1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3863         server lock that was required to assert the lock.  Before
3864         marking the lock as lost, the cache manager checks if the file
3865         has changed on the server.  If the file has not changed, then
3866         the cache manager will attempt to obtain a new server lock
3867         that is sufficient to assert the client side locks for the
3868         file.  If any of these fail, the lock is marked as LOST.
3869         Otherwise, it is left as ACTIVE.
3870
3871       1.2 ACTIVE->DELETED: Lock is released.
3872
3873    2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3874       grants the lock but the lock is yet to be asserted with the AFS
3875       file server.  Once the file server grants the lock, the state
3876       will transition to an ACTIVE lock.
3877
3878       2.1 WAITLOCK->ACTIVE: The server granted the lock.
3879
3880       2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3881         waiting.
3882
3883       2.3 WAITLOCK->LOST: One or more locks from this client were
3884         marked as LOST.  No further locks will be granted to this
3885         client until all lost locks are removed.
3886
3887    3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3888       receives a request for a lock that conflicts with an existing
3889       ACTIVE or WAITLOCK lock.  The lock will be placed in the queue
3890       and will be granted at such time the conflicting locks are
3891       removed, at which point the state will transition to either
3892       WAITLOCK or ACTIVE.
3893
3894       3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed.  The
3895         current serverLock is sufficient to assert this lock, or a
3896         sufficient serverLock is obtained.
3897
3898       3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3899         however the required serverLock is yet to be asserted with the
3900         server.
3901
3902       3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3903         released.
3904
3905       3.5 WAITUNLOCK->LOST: One or more locks from this client were
3906         marked as LOST.  No further locks will be granted to this
3907         client until all lost locks are removed.
3908
3909    4. LOST: A lock L is LOST if the server lock that was required to
3910       assert the lock could not be obtained or if it could not be
3911       extended, or if other locks by the same client were LOST.
3912       Essentially, once a lock is LOST, the contract between the cache
3913       manager and that specific client is no longer valid.
3914
3915       The cache manager rechecks the server lock once every minute and
3916       extends it as appropriate.  If this is not done for 5 minutes,
3917       the AFS file server will release the lock (the 5 minute timeout
3918       is based on current file server code and is fairly arbitrary).
3919       Once released, the lock cannot be re-obtained without verifying
3920       that the contents of the file hasn't been modified since the
3921       time the lock was released.  Re-obtaining the lock without
3922       verifying this may lead to data corruption.  If the lock can not
3923       be obtained safely, then all active locks for the cm_scache_t
3924       are marked as LOST.
3925
3926       4.1 LOST->DELETED: The lock is released.
3927
3928    5. DELETED: The lock is no longer relevant.  Eventually, it will
3929       get removed from the cm_scache_t. In the meantime, it will be
3930       treated as if it does not exist.
3931
3932       5.1 DELETED->not exist: The lock is removed from the
3933         cm_scache_t.
3934
3935    The following are classifications of locks based on their state.
3936
3937    6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK.  These locks
3938       have been accepted by the cache manager, but may or may not have
3939       been granted back to the client.
3940
3941    7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3942
3943    8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3944
3945    Lock operation:
3946
3947    A client C can READ range (Offset,+Length) of a file represented by
3948    cm_scache_t S iff (1):
3949
3950    1. for all _a_ in (Offset,+Length), all of the following is true:
3951
3952        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3953          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3954          shared.
3955
3956        1.2 For each LOST lock L in S->fileLocks such that _a_ in
3957          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3958          Key(C)
3959
3960        (When locks are lost on an cm_scache_t, all locks are lost.  By
3961        4.2 (below), if there is an exclusive LOST lock, then there
3962        can't be any overlapping ACTIVE locks.)
3963
3964    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3965
3966    2. for all _a_ in (Offset,+Length), one of the following is true:
3967
3968        2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3969          does not exist a LOST lock L such that _a_ in
3970          (L->LOffset,+L->LLength).
3971
3972        2.2 Byte _a_ of S is owned by C under lock L (as specified in
3973          1.2) AND L->LockType is exclusive.
3974
3975    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3976
3977    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3978       true:
3979
3980        3.1 If L->LockType is exclusive then there does NOT exist a
3981          ACCEPTED lock M in S->fileLocks such that _a_ in
3982          (M->LOffset,+M->LLength).
3983
3984          (If we count all QUEUED locks then we hit cases such as
3985          cascading waiting locks where the locks later on in the queue
3986          can be granted without compromising file integrity.  On the
3987          other hand if only ACCEPTED locks are considered, then locks
3988          that were received earlier may end up waiting for locks that
3989          were received later to be unlocked. The choice of ACCEPTED
3990          locks was made to mimic the Windows byte range lock
3991          semantics.)
3992
3993        3.2 If L->LockType is shared then for each ACCEPTED lock M in
3994          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3995          M->LockType is shared.
3996
3997    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3998
3999        4.1 M->key != Key(C)
4000
4001        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4002          and (M->LOffset,+M->LLength) do not intersect.
4003
4004          (Note: If a client loses a lock, it loses all locks.
4005          Subsequently, it will not be allowed to obtain any more locks
4006          until all existing LOST locks that belong to the client are
4007          released.  Once all locks are released by a single client,
4008          there exists no further contract between the client and AFS
4009          about the contents of the file, hence the client can then
4010          proceed to obtain new locks and establish a new contract.
4011
4012          This doesn't quite work as you think it should, because most
4013          applications aren't built to deal with losing locks they
4014          thought they once had.  For now, we don't have a good
4015          solution to lost locks.
4016
4017          Also, for consistency reasons, we have to hold off on
4018          granting locks that overlap exclusive LOST locks.)
4019
4020    A client C can only unlock locks L in S->fileLocks which have
4021    L->key == Key(C).
4022
4023    The representation and invariants are as follows:
4024
4025    - Each cm_scache_t structure keeps:
4026
4027        - A queue of byte-range locks (cm_scache_t::fileLocks) which
4028          are of type cm_file_lock_t.
4029
4030        - A record of the highest server-side lock that has been
4031          obtained for this object (cm_scache_t::serverLock), which is
4032          one of (-1), LockRead, LockWrite.
4033
4034        - A count of ACCEPTED exclusive and shared locks that are in the
4035          queue (cm_scache_t::sharedLocks and
4036          cm_scache_t::exclusiveLocks)
4037
4038    - Each cm_file_lock_t structure keeps:
4039
4040        - The type of lock (cm_file_lock_t::LockType)
4041
4042        - The key associated with the lock (cm_file_lock_t::key)
4043
4044        - The offset and length of the lock (cm_file_lock_t::LOffset
4045          and cm_file_lock_t::LLength)
4046
4047        - The state of the lock.
4048
4049        - Time of issuance or last successful extension
4050
4051    Semantic invariants:
4052
4053        I1. The number of ACCEPTED locks in S->fileLocks are
4054            (S->sharedLocks + S->exclusiveLocks)
4055
4056    External invariants:
4057
4058        I3. S->serverLock is the lock that we have asserted with the
4059            AFS file server for this cm_scache_t.
4060
4061        I4. S->serverLock == LockRead iff there is at least one ACTIVE
4062            shared lock, but no ACTIVE exclusive locks.
4063
4064        I5. S->serverLock == LockWrite iff there is at least one ACTIVE
4065            exclusive lock.
4066
4067        I6. If L is a LOST lock, then for each lock M in S->fileLocks,
4068            M->key == L->key IMPLIES M is LOST or DELETED.
4069
4070    --asanka
4071  */
4072
4073 #define IS_LOCK_ACTIVE(lockp)     (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
4074
4075 #define IS_LOCK_WAITLOCK(lockp)   (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_WAITLOCK)
4076
4077 #define IS_LOCK_WAITUNLOCK(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_WAITUNLOCK)
4078
4079 #define IS_LOCK_LOST(lockp)       (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
4080
4081 #define IS_LOCK_DELETED(lockp)    (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
4082
4083 /* unsafe */
4084 #define IS_LOCK_ACCEPTED(lockp)   (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
4085
4086 /* unsafe */
4087 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
4088
4089 /* unsafe */
4090 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
4091
4092 /* unsafe */
4093 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
4094
4095 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
4096 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
4097 #else
4098 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
4099 #endif
4100
4101 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
4102
4103 #if defined(VICED_CAPABILITY_WRITELOCKACL)
4104 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
4105 #else
4106 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
4107
4108 /* This should really be defined in any build that this code is being
4109    compiled. */
4110 #error  VICED_CAPABILITY_WRITELOCKACL not defined.
4111 #endif
4112
4113 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
4114 {
4115     afs_int64 int_begin;
4116     afs_int64 int_end;
4117
4118     int_begin = MAX(pos->offset, neg->offset);
4119     int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
4120
4121     if (int_begin < int_end) {
4122         if (int_begin == pos->offset) {
4123             pos->length = pos->offset + pos->length - int_end;
4124             pos->offset = int_end;
4125         } else if (int_end == pos->offset + pos->length) {
4126             pos->length = int_begin - pos->offset;
4127         }
4128
4129         /* We only subtract ranges if the resulting range is
4130            contiguous.  If we try to support non-contigous ranges, we
4131            aren't actually improving performance. */
4132     }
4133 }
4134
4135 /* Called with scp->rw held.  Returns 0 if all is clear to read the
4136    specified range by the client identified by key.
4137  */
4138 long cm_LockCheckRead(cm_scache_t *scp, 
4139                       LARGE_INTEGER LOffset, 
4140                       LARGE_INTEGER LLength, 
4141                       cm_key_t key)
4142 {
4143 #ifndef ADVISORY_LOCKS
4144
4145     cm_file_lock_t *fileLock;
4146     osi_queue_t *q;
4147     long code = 0;
4148     cm_range_t range;
4149     int substract_ranges = FALSE;
4150
4151     range.offset = LOffset.QuadPart;
4152     range.length = LLength.QuadPart;
4153
4154     /*
4155
4156      1. for all _a_ in (Offset,+Length), all of the following is true:
4157
4158        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4159          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4160          shared.
4161
4162        1.2 For each LOST lock L in S->fileLocks such that _a_ in
4163          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4164          Key(C)
4165
4166     */
4167
4168     lock_ObtainRead(&cm_scacheLock);
4169
4170     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4171         fileLock = 
4172             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4173
4174         if (INTERSECT_RANGE(range, fileLock->range)) {
4175             if (IS_LOCK_ACTIVE(fileLock)) {
4176                 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4177
4178                     /* If there is an active lock for this client, it
4179                        is safe to substract ranges.*/
4180                     cm_LockRangeSubtract(&range, &fileLock->range);
4181                     substract_ranges = TRUE;
4182                 } else {
4183                     if (fileLock->lockType != LockRead) {
4184                         code = CM_ERROR_LOCK_CONFLICT;
4185                         break;
4186                     }
4187
4188                     /* even if the entire range is locked for reading,
4189                        we still can't grant the lock at this point
4190                        because the client may have lost locks. That
4191                        is, unless we have already seen an active lock
4192                        belonging to the client, in which case there
4193                        can't be any lost locks for this client. */
4194                     if (substract_ranges)
4195                         cm_LockRangeSubtract(&range, &fileLock->range);
4196                 }
4197             } else if (IS_LOCK_LOST(fileLock) &&
4198                        (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4199                 code = CM_ERROR_BADFD;
4200                 break;
4201             }
4202         }
4203     }
4204
4205     lock_ReleaseRead(&cm_scacheLock);
4206
4207     osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4208               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4209
4210     return code;
4211
4212 #else
4213
4214     return 0;
4215
4216 #endif
4217 }
4218
4219 /* Called with scp->rw held.  Returns 0 if all is clear to write the
4220    specified range by the client identified by key.
4221  */
4222 long cm_LockCheckWrite(cm_scache_t *scp,
4223                        LARGE_INTEGER LOffset,
4224                        LARGE_INTEGER LLength,
4225                        cm_key_t key)
4226 {
4227 #ifndef ADVISORY_LOCKS
4228
4229     cm_file_lock_t *fileLock;
4230     osi_queue_t *q;
4231     long code = 0;
4232     cm_range_t range;
4233
4234     range.offset = LOffset.QuadPart;
4235     range.length = LLength.QuadPart;
4236
4237     /*
4238    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4239
4240    2. for all _a_ in (Offset,+Length), one of the following is true:
4241
4242        2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4243          lock L such that _a_ in (L->LOffset,+L->LLength).
4244
4245        2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4246          exclusive.
4247     */
4248
4249     lock_ObtainRead(&cm_scacheLock);
4250
4251     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4252         fileLock = 
4253             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4254
4255         if (INTERSECT_RANGE(range, fileLock->range)) {
4256             if (IS_LOCK_ACTIVE(fileLock)) {
4257                 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4258                     if (fileLock->lockType == LockWrite) {
4259
4260                         /* if there is an active lock for this client, it
4261                            is safe to substract ranges */
4262                         cm_LockRangeSubtract(&range, &fileLock->range);
4263                     } else {
4264                         code = CM_ERROR_LOCK_CONFLICT;
4265                         break;
4266                     }
4267                 } else {
4268                     code = CM_ERROR_LOCK_CONFLICT;
4269                     break;
4270                 }
4271             } else if (IS_LOCK_LOST(fileLock)) {
4272                 code = CM_ERROR_BADFD;
4273                 break;
4274             }
4275         }
4276     }
4277
4278     lock_ReleaseRead(&cm_scacheLock);
4279
4280     osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4281               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4282
4283     return code;
4284
4285 #else
4286
4287     return 0;
4288
4289 #endif
4290 }
4291
4292 /* Called with cm_scacheLock write locked */
4293 static cm_file_lock_t * cm_GetFileLock(void) {
4294     cm_file_lock_t * l;
4295
4296     l = (cm_file_lock_t *) cm_freeFileLocks;
4297     if (l) {
4298         osi_QRemove(&cm_freeFileLocks, &l->q);
4299     } else {
4300         l = malloc(sizeof(cm_file_lock_t));
4301         osi_assertx(l, "null cm_file_lock_t");
4302     }
4303
4304     memset(l, 0, sizeof(cm_file_lock_t));
4305
4306     return l;
4307 }
4308
4309 /* Called with cm_scacheLock write locked */
4310 static void cm_PutFileLock(cm_file_lock_t *l) {
4311     osi_QAdd(&cm_freeFileLocks, &l->q);
4312 }
4313
4314 /* called with scp->rw held.  May release it during processing, but
4315    leaves it held on exit. */
4316 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4317                    cm_req_t * reqp) {
4318     long code = 0;
4319     AFSFid tfid;
4320     cm_fid_t cfid;
4321     cm_conn_t * connp;
4322     struct rx_connection * rxconnp;
4323     AFSVolSync volSync;
4324     afs_uint32 reqflags = reqp->flags;
4325
4326     memset(&volSync, 0, sizeof(volSync));
4327
4328     tfid.Volume = scp->fid.volume;
4329     tfid.Vnode = scp->fid.vnode;
4330     tfid.Unique = scp->fid.unique;
4331     cfid = scp->fid;
4332
4333     osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4334
4335     reqp->flags |= CM_REQ_NORETRY;
4336     lock_ReleaseWrite(&scp->rw);
4337
4338     do {
4339         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4340         if (code) 
4341             break;
4342
4343         rxconnp = cm_GetRxConn(connp);
4344         code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4345                              &volSync);
4346         rx_PutConnection(rxconnp);
4347
4348     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4349                         NULL, NULL, code));
4350
4351     code = cm_MapRPCError(code, reqp);
4352     if (code) {
4353         osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4354     } else {
4355         osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4356     }
4357
4358     lock_ObtainWrite(&scp->rw);
4359     reqp->flags = reqflags;
4360     return code;
4361 }
4362
4363 /* called with scp->rw held.  Releases it during processing */
4364 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4365                        cm_req_t * reqp) {
4366     long code = 0;
4367     AFSFid tfid;
4368     cm_fid_t cfid;
4369     cm_conn_t * connp;
4370     struct rx_connection * rxconnp;
4371     AFSVolSync volSync;
4372
4373     memset(&volSync, 0, sizeof(volSync));
4374
4375     tfid.Volume = scp->fid.volume;
4376     tfid.Vnode = scp->fid.vnode;
4377     tfid.Unique = scp->fid.unique;
4378     cfid = scp->fid;
4379
4380     lock_ReleaseWrite(&scp->rw);
4381
4382     osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4383
4384     do {
4385         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4386         if (code) 
4387             break;
4388
4389         rxconnp = cm_GetRxConn(connp);
4390         code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4391         rx_PutConnection(rxconnp);
4392
4393     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4394                         NULL, NULL, code));
4395     code = cm_MapRPCError(code, reqp);
4396     if (code)
4397         osi_Log1(afsd_logp,
4398                  "CALL ReleaseLock FAILURE, code 0x%x", code);
4399     else
4400         osi_Log0(afsd_logp,
4401                  "CALL ReleaseLock SUCCESS");
4402         
4403     lock_ObtainWrite(&scp->rw);
4404
4405     return code;
4406 }
4407
4408 /* called with scp->rw held.  May release it during processing, but
4409    will exit with lock held.
4410
4411    This will return:
4412
4413    - 0 if the user has permission to get the specified lock for the scp
4414
4415    - CM_ERROR_NOACCESS if not
4416
4417    Any other error from cm_SyncOp will be sent down untranslated.
4418
4419    If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4420    phas_insert (if non-NULL) will receive a boolean value indicating
4421    whether the user has INSERT permission or not.
4422 */
4423 long cm_LockCheckPerms(cm_scache_t * scp,
4424                        int lock_type,
4425                        cm_user_t * userp,
4426                        cm_req_t * reqp,
4427                        int * phas_insert)
4428 {
4429     long rights = 0;
4430     long code = 0, code2 = 0;
4431
4432     /* lock permissions are slightly tricky because of the 'i' bit.
4433        If the user has PRSFS_LOCK, she can read-lock the file.  If the
4434        user has PRSFS_WRITE, she can write-lock the file.  However, if
4435        the user has PRSFS_INSERT, then she can write-lock new files,
4436        but not old ones.  Since we don't have information about
4437        whether a file is new or not, we assume that if the user owns
4438        the scp, then she has the permissions that are granted by
4439        PRSFS_INSERT. */
4440
4441     osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4442              scp, lock_type, userp);
4443
4444     if (lock_type == LockRead)
4445         rights |= PRSFS_LOCK;
4446     else if (lock_type == LockWrite)
4447         rights |= PRSFS_WRITE | PRSFS_LOCK;
4448     else {
4449         /* hmmkay */
4450         osi_assertx(FALSE, "invalid lock type");
4451         return 0;
4452     }
4453
4454     if (phas_insert)
4455         *phas_insert = FALSE;
4456
4457     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4458                      CM_SCACHESYNC_GETSTATUS |
4459                      CM_SCACHESYNC_NEEDCALLBACK);
4460
4461     if (phas_insert && scp->creator == userp) {
4462
4463         /* If this file was created by the user, then we check for
4464            PRSFS_INSERT.  If the file server is recent enough, then
4465            this should be sufficient for her to get a write-lock (but
4466            not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4467            indicates whether a file server supports getting write
4468            locks when the user only has PRSFS_INSERT. 
4469            
4470            If the file was not created by the user we skip the check
4471            because the INSERT bit will not apply to this user even
4472            if it is set.
4473          */
4474
4475         code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4476                          CM_SCACHESYNC_GETSTATUS |
4477                          CM_SCACHESYNC_NEEDCALLBACK);
4478
4479         if (code2 == CM_ERROR_NOACCESS) {
4480             osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4481         } else {
4482             *phas_insert = TRUE;
4483             osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4484         }
4485     }
4486
4487     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4488
4489     osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4490
4491     return code;
4492 }
4493
4494 /* called with scp->rw held */
4495 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4496              LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4497              cm_key_t key,
4498              int allowWait, cm_user_t *userp, cm_req_t *reqp,
4499              cm_file_lock_t **lockpp)
4500 {
4501     long code = 0;
4502     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4503     cm_file_lock_t *fileLock;
4504     osi_queue_t *q;
4505     cm_range_t range;
4506     int wait_unlock = FALSE;
4507     int force_client_lock = FALSE;
4508
4509     osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4510              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4511     osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait, 
4512              key.process_id, key.session_id, key.file_id);
4513
4514     /*
4515    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4516
4517    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4518       true:
4519
4520        3.1 If L->LockType is exclusive then there does NOT exist a
4521          ACCEPTED lock M in S->fileLocks such that _a_ in
4522          (M->LOffset,+M->LLength).
4523
4524        3.2 If L->LockType is shared then for each ACCEPTED lock M in
4525          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4526          M->LockType is shared.
4527
4528    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4529
4530        4.1 M->key != Key(C)
4531
4532        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4533          and (M->LOffset,+M->LLength) do not intersect.
4534     */
4535
4536     range.offset = LOffset.QuadPart;
4537     range.length = LLength.QuadPart;
4538
4539     lock_ObtainRead(&cm_scacheLock);
4540
4541     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4542         fileLock =
4543             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4544
4545         if (IS_LOCK_LOST(fileLock)) {
4546             if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4547                 code = CM_ERROR_BADFD;
4548                 break;
4549             } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4550                 code = CM_ERROR_WOULDBLOCK;
4551                 wait_unlock = TRUE;
4552                 break;
4553             }
4554         }
4555
4556         /* we don't need to check for deleted locks here since deleted
4557            locks are dequeued from scp->fileLocks */
4558         if (IS_LOCK_ACCEPTED(fileLock) &&
4559            INTERSECT_RANGE(range, fileLock->range)) {
4560
4561             if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4562                 fileLock->lockType != LockRead) {
4563                 wait_unlock = TRUE;
4564                 code = CM_ERROR_WOULDBLOCK;
4565                 break;
4566             }
4567         }
4568     }
4569
4570     lock_ReleaseRead(&cm_scacheLock);
4571
4572     if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4573         if (Which == scp->serverLock ||
4574            (Which == LockRead && scp->serverLock == LockWrite)) {
4575
4576             int has_insert = 0;
4577
4578             /* we already have the lock we need */
4579             osi_Log3(afsd_logp, "   we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]", 
4580                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4581
4582             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4583
4584             /* special case: if we don't have permission to read-lock
4585                the file, then we force a clientside lock.  This is to
4586                compensate for applications that obtain a read-lock for
4587                reading files off of directories that don't grant
4588                read-locks to the user. */
4589             if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4590
4591                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4592                     osi_Log0(afsd_logp, "   User has no read-lock perms, but has INSERT perms.");
4593                     code = 0;
4594                 } else {
4595                     osi_Log0(afsd_logp, "   User has no read-lock perms. Forcing client-side lock");
4596                     force_client_lock = TRUE;
4597                 }
4598             }
4599
4600         } else if ((scp->exclusiveLocks > 0) ||
4601                    (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4602             int has_insert = 0;
4603
4604             /* We are already waiting for some other lock.  We should
4605                wait for the daemon to catch up instead of generating a
4606                flood of SetLock calls. */
4607             osi_Log3(afsd_logp, "   already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4608                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4609
4610             /* see if we have permission to create the lock in the
4611                first place. */
4612             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4613             if (code == 0)
4614                 code = CM_ERROR_WOULDBLOCK;
4615             else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4616
4617                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4618                     osi_Log0(afsd_logp,
4619                              "   User has no read-lock perms, but has INSERT perms.");
4620                     code = CM_ERROR_WOULDBLOCK;
4621                 } else {
4622                     osi_Log0(afsd_logp,
4623                              "   User has no read-lock perms. Forcing client-side lock");
4624                     force_client_lock = TRUE;
4625                 }
4626             }
4627
4628             /* leave any other codes as-is */
4629
4630         } else {
4631             int newLock;
4632             int check_data_version = FALSE;
4633             int has_insert = 0;
4634
4635             /* first check if we have permission to elevate or obtain
4636                the lock. */
4637             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4638             if (code) {
4639                 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4640                     (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4641                     osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
4642                     force_client_lock = TRUE;
4643                 }
4644                 goto check_code;
4645             }
4646
4647             /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4648
4649             if (scp->serverLock == LockRead && Which == LockWrite) {
4650
4651                 /* We want to escalate the lock to a LockWrite.
4652                  * Unfortunately that's not really possible without
4653                  * letting go of the current lock.  But for now we do
4654                  * it anyway. */
4655
4656                 osi_Log0(afsd_logp,
4657                          "   attempting to UPGRADE from LockRead to LockWrite.");
4658                 osi_Log1(afsd_logp,
4659                          "   dataVersion on scp: %I64d", scp->dataVersion);
4660
4661                 /* we assume at this point (because scp->serverLock
4662                    was valid) that we had a valid server lock. */
4663                 scp->lockDataVersion = scp->dataVersion;
4664                 check_data_version = TRUE;
4665         
4666                 code = cm_IntReleaseLock(scp, userp, reqp);
4667
4668                 if (code) {
4669                     /* We couldn't release the lock */
4670                     goto check_code;
4671                 } else {
4672                     scp->serverLock = -1;
4673                 }
4674             }
4675
4676             /* We need to obtain a server lock of type Which in order
4677              * to assert this file lock */
4678 #ifndef AGGRESSIVE_LOCKS
4679             newLock = Which;
4680 #else
4681             newLock = LockWrite;
4682 #endif
4683
4684             code = cm_IntSetLock(scp, userp, newLock, reqp);
4685
4686 #ifdef AGGRESSIVE_LOCKS
4687             if ((code == CM_ERROR_WOULDBLOCK ||
4688                  code == CM_ERROR_NOACCESS) && newLock != Which) {
4689                 /* we wanted LockRead.  We tried LockWrite. Now try
4690                  * LockRead again */
4691                 newLock = Which;
4692
4693                 /* am I sane? */
4694                 osi_assertx(newLock == LockRead, "lock type not read");
4695
4696                 code = cm_IntSetLock(scp, userp, newLock, reqp);
4697             }
4698 #endif
4699
4700             if (code == CM_ERROR_NOACCESS) {
4701                 if (Which == LockRead) {
4702                     if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4703                         long tcode;
4704                         /* We requested a read-lock, but we have permission to
4705                          * get a write-lock. Try that */
4706
4707                         tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4708
4709                         if (tcode == 0) {
4710                             newLock = LockWrite;
4711
4712                             osi_Log0(afsd_logp, "   User has 'i' perms and the request was for a LockRead.  Trying to get a LockWrite instead");
4713
4714                             code = cm_IntSetLock(scp, userp, newLock, reqp);
4715                         }
4716                     } else {
4717                         osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
4718                         force_client_lock = TRUE;
4719                     }
4720                 } else if (Which == LockWrite &&
4721                            scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4722                     long tcode;
4723
4724                     /* Special case: if the lock request was for a
4725                      * LockWrite and the user owns the file and we weren't
4726                      * allowed to obtain the serverlock, we either lost a
4727                      * race (the permissions changed from under us), or we
4728                      * have 'i' bits, but we aren't allowed to lock the
4729                      * file. */
4730
4731                     /* check if we lost a race... */
4732                     tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4733
4734                     if (tcode == 0) {
4735                         osi_Log0(afsd_logp, "   User has 'i' perms but can't obtain write locks. Using client-side locks.");
4736                         force_client_lock = TRUE;
4737                     }
4738                 }
4739             }
4740
4741             if (code == 0 && check_data_version &&
4742                scp->dataVersion != scp->lockDataVersion) {
4743                 /* We lost a race.  Although we successfully obtained
4744                  * a lock, someone modified the file in between.  The
4745                  * locks have all been technically lost. */
4746
4747                 osi_Log0(afsd_logp,
4748                          "  Data version mismatch while upgrading lock.");
4749                 osi_Log2(afsd_logp,
4750                          "  Data versions before=%I64d, after=%I64d",
4751                          scp->lockDataVersion,
4752                          scp->dataVersion);
4753                 osi_Log1(afsd_logp,
4754                          "  Releasing stale lock for scp 0x%x", scp);
4755
4756                 code = cm_IntReleaseLock(scp, userp, reqp);
4757
4758                 scp->serverLock = -1;
4759
4760                 code = CM_ERROR_INVAL;
4761             } else if (code == 0) {
4762                 scp->serverLock = newLock;
4763                 scp->lockDataVersion = scp->dataVersion;
4764             }
4765
4766             if (code != 0 &&
4767                 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4768                 scp->serverLock == -1) {
4769                 /* Oops. We lost the lock. */
4770                 cm_LockMarkSCacheLost(scp);
4771             }
4772         }
4773     } else if (code == 0) {     /* server locks not enabled */
4774         osi_Log0(afsd_logp,
4775                  "  Skipping server lock for scp");
4776     }
4777
4778  check_code:
4779
4780     if (code != 0 && !force_client_lock) {
4781         /* Special case error translations
4782
4783            Applications don't expect certain errors from a
4784            LockFile/UnlockFile call.  We need to translate some error
4785            code to codes that apps expect and handle. */
4786
4787         /* We shouldn't actually need to handle this case since we
4788            simulate locks for RO scps anyway. */
4789         if (code == CM_ERROR_READONLY) {
4790             osi_Log0(afsd_logp, "   Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4791             code = CM_ERROR_NOACCESS;
4792         }
4793     }
4794
4795     if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4796         force_client_lock) {
4797
4798         /* clear the error if we are forcing a client lock, so we
4799            don't get confused later. */
4800         if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4801             code = 0;
4802
4803         lock_ObtainWrite(&cm_scacheLock);
4804         fileLock = cm_GetFileLock();
4805         lock_ReleaseWrite(&cm_scacheLock);
4806 #ifdef DEBUG
4807         fileLock->fid = scp->fid;
4808 #endif
4809         fileLock->key = key;
4810         fileLock->lockType = Which;
4811         cm_HoldUser(userp);
4812         fileLock->userp = userp;
4813         fileLock->range = range;
4814         fileLock->flags = (code == 0 ? 0 : 
4815                            ((wait_unlock)?
4816                             CM_FILELOCK_FLAG_WAITUNLOCK :
4817                             CM_FILELOCK_FLAG_WAITLOCK));
4818
4819         if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4820             fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4821
4822         fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4823
4824         lock_ObtainWrite(&cm_scacheLock);
4825         osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4826         cm_HoldSCacheNoLock(scp);
4827         fileLock->scp = scp;
4828         osi_QAdd(&cm_allFileLocks, &fileLock->q);
4829         lock_ReleaseWrite(&cm_scacheLock);
4830
4831         if (code != 0) {
4832             *lockpp = fileLock;
4833         }
4834
4835         if (IS_LOCK_CLIENTONLY(fileLock)) {
4836             scp->clientLocks++;
4837         } else if (IS_LOCK_ACCEPTED(fileLock)) {
4838             if (Which == LockRead)
4839                 scp->sharedLocks++;
4840             else
4841                 scp->exclusiveLocks++;
4842         }
4843
4844         osi_Log3(afsd_logp,
4845                  "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4846                  fileLock, fileLock->flags, scp);
4847         osi_Log4(afsd_logp,
4848                  "   exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4849                  scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4850                  (int)(signed char) scp->serverLock);
4851     } else {
4852         osi_Log1(afsd_logp,
4853                  "cm_Lock Rejecting lock (code = 0x%x)", code);
4854     }
4855
4856     /* Convert from would block to lock not granted */
4857     if (code == CM_ERROR_WOULDBLOCK)
4858         code = CM_ERROR_LOCK_NOT_GRANTED;
4859
4860     return code;
4861 }
4862
4863 /* Called with scp->rw held */
4864 long cm_UnlockByKey(cm_scache_t * scp,
4865                     cm_key_t key,
4866                     int flags,
4867                     cm_user_t * userp,
4868                      cm_req_t * reqp)
4869 {
4870     long code = 0;
4871     cm_file_lock_t *fileLock;
4872     osi_queue_t *q, *qn;
4873     int n_unlocks = 0;
4874
4875     osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
4876              scp, key.process_id, key.session_id, key.file_id);
4877     osi_Log1(afsd_logp, "    flags=0x%x", flags);
4878
4879     lock_ObtainWrite(&cm_scacheLock);
4880
4881     for (q = scp->fileLocksH; q; q = qn) {
4882         qn = osi_QNext(q);
4883
4884         fileLock = (cm_file_lock_t *)
4885             ((char *) q - offsetof(cm_file_lock_t, fileq));
4886
4887 #ifdef DEBUG
4888         osi_Log4(afsd_logp, "   Checking lock[0x%x] range[%d,+%d] type[%d]",
4889                  fileLock,
4890                  (unsigned long) fileLock->range.offset,
4891                  (unsigned long) fileLock->range.length,
4892                 fileLock->lockType);
4893         osi_Log4(afsd_logp, "     key<0x%x, 0x%x, 0x%x> flags[0x%x]",
4894                  fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
4895                  fileLock->flags);
4896
4897         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4898             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4899             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4900                      fileLock->fid.cell,
4901                      fileLock->fid.volume,
4902                      fileLock->fid.vnode,
4903                      fileLock->fid.unique);
4904             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4905                      fileLock->scp->fid.cell,
4906                      fileLock->scp->fid.volume,
4907                      fileLock->scp->fid.vnode,
4908                      fileLock->scp->fid.unique);
4909             osi_assertx(FALSE, "invalid fid value");
4910         }
4911 #endif
4912
4913         if (!IS_LOCK_DELETED(fileLock) &&
4914             cm_KeyEquals(&fileLock->key, &key, flags)) {
4915             osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4916                     fileLock->range.offset,
4917                     fileLock->range.length,
4918                     fileLock->lockType);
4919
4920             if (scp->fileLocksT == q)
4921                 scp->fileLocksT = osi_QPrev(q);
4922             osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4923
4924             if (IS_LOCK_CLIENTONLY(fileLock)) {
4925                 scp->clientLocks--;
4926             } else if (IS_LOCK_ACCEPTED(fileLock)) {
4927                 if (fileLock->lockType == LockRead)
4928                     scp->sharedLocks--;
4929                 else
4930                     scp->exclusiveLocks--;
4931             }
4932
4933             fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4934
4935             cm_ReleaseUser(fileLock->userp);
4936             cm_ReleaseSCacheNoLock(scp);
4937
4938             fileLock->userp = NULL;
4939             fileLock->scp = NULL;
4940
4941             n_unlocks++;
4942         }
4943     }
4944
4945     lock_ReleaseWrite(&cm_scacheLock);
4946
4947     if (n_unlocks == 0) {
4948         osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4949         osi_Log3(afsd_logp, "   Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4950                  scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4951         
4952         return 0;
4953     }
4954
4955     osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4956
4957     osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4958     osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4959     osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4960
4961     if (!SERVERLOCKS_ENABLED(scp)) {
4962         osi_Log0(afsd_logp, "  Skipping server lock for scp");
4963         goto done;
4964     }
4965
4966     /* Ideally we would go through the rest of the locks to determine
4967      * if one or more locks that were formerly in WAITUNLOCK can now
4968      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4969      * scp->sharedLocks accordingly.  However, the retrying of locks
4970      * in that manner is done cm_RetryLock() manually.
4971      */
4972
4973     if (scp->serverLock == LockWrite &&
4974         scp->exclusiveLocks == 0 &&
4975         scp->sharedLocks > 0) {
4976         /* The serverLock should be downgraded to LockRead */
4977         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
4978
4979         /* Make sure there are no dirty buffers left. */
4980         code = cm_FSync(scp, userp, reqp, TRUE);
4981
4982         /* since scp->serverLock looked sane, we are going to assume
4983            that we have a valid server lock. */
4984         scp->lockDataVersion = scp->dataVersion;
4985         osi_Log1(afsd_logp, "  dataVersion on scp = %I64d", scp->dataVersion);
4986
4987         code = cm_IntReleaseLock(scp, userp, reqp);
4988
4989         if (code) {
4990             /* so we couldn't release it.  Just let the lock be for now */
4991             code = 0;
4992             goto done;
4993         } else {
4994             scp->serverLock = -1;
4995         }
4996
4997         code = cm_IntSetLock(scp, userp, LockRead, reqp);
4998
4999         if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5000             scp->serverLock = LockRead;
5001         } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5002             /* We lost a race condition.  Although we have a valid
5003                lock on the file, the data has changed and essentially
5004                we have lost the lock we had during the transition. */
5005
5006             osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
5007             osi_Log2(afsd_logp, "  Data versions before=%I64d, after=%I64d",
5008                      scp->lockDataVersion,
5009                      scp->dataVersion);
5010             
5011             code = cm_IntReleaseLock(scp, userp, reqp);
5012
5013             code = CM_ERROR_INVAL;
5014             scp->serverLock = -1;
5015         }
5016
5017         if (code != 0 &&
5018             (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5019                 (scp->serverLock == -1)) {
5020                 /* Oopsie */
5021                 cm_LockMarkSCacheLost(scp);
5022             }
5023
5024         /* failure here has no bearing on the return value of
5025            cm_Unlock() */
5026         code = 0;
5027
5028     } else if (scp->serverLock != (-1) &&
5029               scp->exclusiveLocks == 0 &&
5030               scp->sharedLocks == 0) {
5031         /* The serverLock should be released entirely */
5032
5033         if (scp->serverLock == LockWrite) {
5034             /* Make sure there are no dirty buffers left. */
5035             code = cm_FSync(scp, userp, reqp, TRUE);
5036         }
5037
5038         code = cm_IntReleaseLock(scp, userp, reqp);
5039
5040         if (code == 0)
5041             scp->serverLock = (-1);
5042     }
5043
5044  done:
5045
5046     osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
5047     osi_Log4(afsd_logp, "   Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5048              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5049              (int)(signed char) scp->serverLock);
5050
5051     return code;
5052 }
5053
5054 long cm_Unlock(cm_scache_t *scp, 
5055                unsigned char sLockType,
5056                LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
5057                cm_key_t key, 
5058                afs_uint32 flags,
5059                cm_user_t *userp, 
5060                cm_req_t *reqp)
5061 {
5062     long code = 0;
5063     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
5064     cm_file_lock_t *fileLock;
5065     osi_queue_t *q;
5066     int release_userp = FALSE;
5067     int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
5068     int lock_found  = 0;
5069     LARGE_INTEGER RangeEnd;
5070
5071     osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
5072              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
5073     osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
5074              key.process_id, key.session_id, key.file_id, flags);
5075
5076     if (!exact_match)
5077         RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
5078
5079   try_again:
5080     lock_ObtainRead(&cm_scacheLock);
5081
5082     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5083         fileLock = (cm_file_lock_t *)
5084             ((char *) q - offsetof(cm_file_lock_t, fileq));
5085
5086 #ifdef DEBUG
5087         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5088             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5089             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5090                      fileLock->fid.cell,
5091                      fileLock->fid.volume,
5092                      fileLock->fid.vnode,
5093                      fileLock->fid.unique);
5094             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5095                      fileLock->scp->fid.cell,
5096                      fileLock->scp->fid.volume,
5097                      fileLock->scp->fid.vnode,
5098                      fileLock->scp->fid.unique);
5099             osi_assertx(FALSE, "invalid fid value");
5100         }
5101 #endif
5102         if (exact_match) {
5103             if (!IS_LOCK_DELETED(fileLock) &&
5104                  cm_KeyEquals(&fileLock->key, &key, 0) &&
5105                  fileLock->range.offset == LOffset.QuadPart &&
5106                  fileLock->range.length == LLength.QuadPart) {
5107                 lock_found = 1;
5108                 break;
5109             }
5110         } else {
5111
5112             if (!IS_LOCK_DELETED(fileLock) &&
5113                  cm_KeyEquals(&fileLock->key, &key, 0) &&
5114                  fileLock->range.offset >= LOffset.QuadPart &&
5115                  fileLock->range.offset < RangeEnd.QuadPart &&
5116                  (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5117                 lock_found = 1;
5118                 break;
5119             }
5120         }
5121     }
5122
5123     if (!q) {
5124         lock_ReleaseRead(&cm_scacheLock);
5125
5126         if (lock_found && !exact_match) {
5127             code = 0;
5128             goto done;
5129         } else {
5130             osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5131         
5132             /* The lock didn't exist anyway. *shrug* */
5133             return CM_ERROR_RANGE_NOT_LOCKED;
5134         }
5135     }
5136
5137     /* discard lock record */
5138     lock_ConvertRToW(&cm_scacheLock);
5139     if (scp->fileLocksT == q)
5140         scp->fileLocksT = osi_QPrev(q);
5141     osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5142
5143     /*
5144      * Don't delete it here; let the daemon delete it, to simplify
5145      * the daemon's traversal of the list.
5146      */
5147
5148     if (IS_LOCK_CLIENTONLY(fileLock)) {
5149         scp->clientLocks--;
5150     } else if (IS_LOCK_ACCEPTED(fileLock)) {
5151         if (fileLock->lockType == LockRead)
5152             scp->sharedLocks--;
5153         else
5154             scp->exclusiveLocks--;
5155     }
5156
5157     fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5158     if (userp != NULL) {
5159         cm_ReleaseUser(fileLock->userp);
5160     } else {
5161         userp = fileLock->userp;
5162         release_userp = TRUE;
5163     }
5164     fileLock->userp = NULL;
5165     cm_ReleaseSCacheNoLock(scp);
5166     fileLock->scp = NULL;
5167     lock_ReleaseWrite(&cm_scacheLock);
5168
5169     if (!SERVERLOCKS_ENABLED(scp)) {
5170         osi_Log0(afsd_logp, "   Skipping server locks for scp");
5171         goto done;
5172     }
5173
5174     /* Ideally we would go through the rest of the locks to determine
5175      * if one or more locks that were formerly in WAITUNLOCK can now
5176      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
5177      * scp->sharedLocks accordingly.  However, the retrying of locks
5178      * in that manner is done cm_RetryLock() manually.
5179      */
5180
5181     if (scp->serverLock == LockWrite &&
5182         scp->exclusiveLocks == 0 &&
5183         scp->sharedLocks > 0) {
5184
5185         /* The serverLock should be downgraded to LockRead */
5186         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
5187
5188         /* Make sure there are no dirty buffers left. */
5189         code = cm_FSync(scp, userp, reqp, TRUE);
5190
5191         /* Since we already had a lock, we assume that there is a
5192            valid server lock. */
5193         scp->lockDataVersion = scp->dataVersion;
5194         osi_Log1(afsd_logp, "   dataVersion on scp is %I64d", scp->dataVersion);
5195
5196         /* before we downgrade, make sure that we have enough
5197            permissions to get the read lock. */
5198         code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5199         if (code != 0) {
5200
5201             osi_Log0(afsd_logp, "  SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5202
5203             code = 0;
5204             goto done;
5205         }
5206
5207         code = cm_IntReleaseLock(scp, userp, reqp);
5208
5209         if (code) {
5210             /* so we couldn't release it.  Just let the lock be for now */
5211             code = 0;
5212             goto done;
5213         } else {
5214             scp->serverLock = -1;
5215         }
5216
5217         code = cm_IntSetLock(scp, userp, LockRead, reqp);
5218
5219         if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5220             scp->serverLock = LockRead;
5221         } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5222             /* Lost a race.  We obtained a new lock, but that is
5223                meaningless since someone modified the file
5224                inbetween. */
5225
5226             osi_Log0(afsd_logp,
5227                      "Data version mismatch while downgrading lock");
5228             osi_Log2(afsd_logp,
5229                      "  Data versions before=%I64d, after=%I64d",
5230                      scp->lockDataVersion,
5231                      scp->dataVersion);
5232             
5233             code = cm_IntReleaseLock(scp, userp, reqp);
5234
5235             scp->serverLock = -1;
5236             code = CM_ERROR_INVAL;
5237         }
5238
5239         if (code != 0 &&
5240             (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5241                 (scp->serverLock == -1)) {
5242                 /* Oopsie */
5243                 cm_LockMarkSCacheLost(scp);
5244             }
5245
5246         /* failure here has no bearing on the return value of
5247            cm_Unlock() */
5248         code = 0;
5249
5250     } else if (scp->serverLock != (-1) &&
5251               scp->exclusiveLocks == 0 &&
5252               scp->sharedLocks == 0) {
5253         /* The serverLock should be released entirely */
5254
5255         if (scp->serverLock == LockWrite) {
5256             /* Make sure there are no dirty buffers left. */
5257             code = cm_FSync(scp, userp, reqp, TRUE);
5258         }
5259
5260         code = cm_IntReleaseLock(scp, userp, reqp);
5261
5262         if (code == 0) {
5263             scp->serverLock = (-1);
5264         }
5265     }
5266
5267     if (release_userp) {
5268         cm_ReleaseUser(userp);
5269         release_userp = FALSE;
5270     }
5271
5272     if (!exact_match)
5273         goto try_again;         /* might be more than one lock in the range */
5274
5275  done:
5276
5277     osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5278     osi_Log4(afsd_logp, "  leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5279              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5280              (int)(signed char) scp->serverLock);
5281
5282     return code;
5283 }
5284
5285 /* called with scp->rw held */
5286 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5287 {
5288     cm_file_lock_t *fileLock;
5289     osi_queue_t *q;
5290
5291     osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5292
5293     /* cm_scacheLock needed because we are modifying fileLock->flags */
5294     lock_ObtainWrite(&cm_scacheLock);
5295
5296     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5297         fileLock = 
5298             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5299
5300         if (IS_LOCK_ACTIVE(fileLock) &&
5301             !IS_LOCK_CLIENTONLY(fileLock)) {
5302             if (fileLock->lockType == LockRead)
5303                 scp->sharedLocks--;
5304             else
5305                 scp->exclusiveLocks--;
5306
5307             fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5308         }
5309     }
5310
5311     scp->serverLock = -1;
5312     scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5313     lock_ReleaseWrite(&cm_scacheLock);
5314 }
5315
5316 /* Called with no relevant locks held */
5317 void cm_CheckLocks()
5318 {
5319     osi_queue_t *q, *nq;
5320     cm_file_lock_t *fileLock;
5321     cm_req_t req;
5322     AFSFid tfid;
5323     AFSVolSync volSync;
5324     cm_conn_t *connp;
5325     long code;
5326     struct rx_connection * rxconnp;
5327     cm_scache_t * scp;
5328
5329     memset(&volSync, 0, sizeof(volSync));
5330
5331     cm_InitReq(&req);
5332
5333     lock_ObtainWrite(&cm_scacheLock);
5334
5335     cm_lockRefreshCycle++;
5336
5337     osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5338
5339     for (q = cm_allFileLocks; q; q = nq) {
5340         fileLock = (cm_file_lock_t *) q;
5341         nq = osi_QNext(q);
5342         code = -1;
5343
5344         if (IS_LOCK_DELETED(fileLock)) {
5345
5346             osi_QRemove(&cm_allFileLocks, q);
5347             cm_PutFileLock(fileLock);
5348
5349         } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5350
5351             /* Server locks must have been enabled for us to have
5352                received an active non-client-only lock. */
5353             osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5354
5355             scp = fileLock->scp;
5356             osi_assertx(scp != NULL, "null cm_scache_t");
5357
5358             cm_HoldSCacheNoLock(scp);
5359
5360 #ifdef DEBUG
5361             if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5362                 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5363                 osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5364                          fileLock->fid.cell,
5365                          fileLock->fid.volume,
5366                          fileLock->fid.vnode,
5367                          fileLock->fid.unique);
5368                 osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5369                          fileLock->scp->fid.cell,
5370                          fileLock->scp->fid.volume,
5371                          fileLock->scp->fid.vnode,
5372                          fileLock->scp->fid.unique);
5373                 osi_assertx(FALSE, "invalid fid");
5374             }
5375 #endif
5376             /* Server locks are extended once per scp per refresh
5377                cycle. */
5378             if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5379
5380                 int scp_done = FALSE;
5381
5382                 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5383
5384                 lock_ReleaseWrite(&cm_scacheLock);
5385                 lock_ObtainWrite(&scp->rw);
5386
5387                 /* did the lock change while we weren't holding the lock? */
5388                 if (!IS_LOCK_ACTIVE(fileLock))
5389                     goto post_syncopdone;
5390
5391                 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5392                                  CM_SCACHESYNC_NEEDCALLBACK
5393                                  | CM_SCACHESYNC_GETSTATUS
5394                                  | CM_SCACHESYNC_LOCK);
5395
5396                 if (code) {
5397                     osi_Log1(afsd_logp,
5398                              "cm_CheckLocks SyncOp failure code 0x%x", code);
5399                     goto post_syncopdone;
5400                 }
5401
5402                 /* cm_SyncOp releases scp->rw during which the lock
5403                    may get released. */
5404                 if (!IS_LOCK_ACTIVE(fileLock))
5405                     goto pre_syncopdone;
5406
5407                 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5408                     cm_fid_t cfid;
5409                     cm_user_t * userp;
5410
5411                     tfid.Volume = scp->fid.volume;
5412                     tfid.Vnode = scp->fid.vnode;
5413                     tfid.Unique = scp->fid.unique;
5414                     cfid = scp->fid;
5415                     userp = fileLock->userp;
5416                     
5417                     osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d", 
5418                              fileLock,
5419                              scp,
5420                              (int) scp->serverLock);
5421
5422                     lock_ReleaseWrite(&scp->rw);
5423
5424                     do {
5425                         code = cm_ConnFromFID(&cfid, userp,
5426                                        &req, &connp);
5427                         if (code) 
5428                             break;
5429
5430                         rxconnp = cm_GetRxConn(connp);
5431                         code = RXAFS_ExtendLock(rxconnp, &tfid,
5432                                                 &volSync);
5433                         rx_PutConnection(rxconnp);
5434
5435                         osi_Log1(afsd_logp, "   ExtendLock returns %d", code);
5436
5437                     } while (cm_Analyze(connp, userp, &req,
5438                                         &cfid, &volSync, NULL, NULL,
5439                                         code));
5440
5441                     code = cm_MapRPCError(code, &req);
5442
5443                     lock_ObtainWrite(&scp->rw);
5444
5445                     if (code) {
5446                         osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5447                     } else {
5448                         osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5449                         scp->lockDataVersion = scp->dataVersion;
5450                     }
5451
5452                     if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5453                         scp->lockDataVersion == scp->dataVersion) {
5454                         int lockType;
5455
5456                         lockType =
5457                             (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5458
5459                         /* we might still have a chance to obtain a
5460                            new lock */
5461
5462                         code = cm_IntSetLock(scp, userp, lockType, &req);
5463
5464                         if (code) {
5465                             code = CM_ERROR_INVAL;
5466                         } else if (scp->lockDataVersion != scp->dataVersion) {
5467
5468                             /* now check if we still have the file at
5469                                the right data version. */
5470                             osi_Log1(afsd_logp,
5471                                      "Data version mismatch on scp 0x%p",
5472                                      scp);
5473                             osi_Log2(afsd_logp,
5474                                      "   Data versions: before=%I64d, after=%I64d",
5475                                      scp->lockDataVersion,
5476                                      scp->dataVersion);
5477
5478                             code = cm_IntReleaseLock(scp, userp, &req);
5479
5480                             code = CM_ERROR_INVAL;
5481                         }
5482                     }
5483
5484                     if (code == EINVAL || code == CM_ERROR_INVAL ||
5485                         code == CM_ERROR_BADFD) {
5486                         cm_LockMarkSCacheLost(scp);
5487                     }
5488
5489                 } else {
5490                     /* interestingly, we have found an active lock
5491                        belonging to an scache that has no
5492                        serverLock */
5493                     cm_LockMarkSCacheLost(scp);
5494                 }
5495
5496                 scp_done = TRUE;
5497
5498             pre_syncopdone:
5499
5500                 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5501
5502             post_syncopdone:
5503                 lock_ReleaseWrite(&scp->rw);
5504
5505                 lock_ObtainWrite(&cm_scacheLock);
5506
5507                 if (code == 0) {
5508                     fileLock->lastUpdate = time(NULL);
5509                 }
5510                 
5511                 if (scp_done)
5512                     scp->lastRefreshCycle = cm_lockRefreshCycle;
5513
5514             } else {
5515                 /* we have already refreshed the locks on this scp */
5516                 fileLock->lastUpdate = time(NULL);
5517             }
5518
5519             cm_ReleaseSCacheNoLock(scp);
5520
5521         } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5522             /* TODO: Check callbacks */
5523         }
5524     }
5525
5526     lock_ReleaseWrite(&cm_scacheLock);
5527     osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5528 }
5529
5530 /* NOT called with scp->rw held. */
5531 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5532 {
5533     long code = 0;
5534     cm_scache_t *scp = NULL;
5535     cm_file_lock_t *fileLock;
5536     osi_queue_t *q;
5537     cm_req_t req;
5538     int newLock = -1;
5539     int force_client_lock = FALSE;
5540     int has_insert = FALSE;
5541     int check_data_version = FALSE;
5542
5543     cm_InitReq(&req);
5544
5545     if (client_is_dead) {
5546         code = CM_ERROR_TIMEDOUT;
5547         goto updateLock;
5548     }
5549
5550     lock_ObtainRead(&cm_scacheLock);
5551
5552     osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5553     osi_Log4(afsd_logp, "    offset(%x:%x) length(%x:%x)",
5554              (unsigned)(oldFileLock->range.offset >> 32),
5555              (unsigned)(oldFileLock->range.offset & 0xffffffff),
5556              (unsigned)(oldFileLock->range.length >> 32),
5557              (unsigned)(oldFileLock->range.length & 0xffffffff));
5558     osi_Log4(afsd_logp, "    key<0x%x,0x%x,0x%x> flags=%x",
5559              oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5560              (unsigned)(oldFileLock->flags));
5561
5562     /* if the lock has already been granted, then we have nothing to do */
5563     if (IS_LOCK_ACTIVE(oldFileLock)) {
5564         lock_ReleaseRead(&cm_scacheLock);
5565         osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5566         return 0;
5567     }
5568
5569     /* we can't do anything with lost or deleted locks at the moment. */
5570     if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5571         code = CM_ERROR_BADFD;
5572         osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5573         lock_ReleaseRead(&cm_scacheLock);
5574         goto updateLock;
5575     }
5576
5577     scp = oldFileLock->scp;
5578
5579     osi_assertx(scp != NULL, "null cm_scache_t");
5580
5581     lock_ReleaseRead(&cm_scacheLock);
5582     lock_ObtainWrite(&scp->rw);
5583
5584     code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5585                              oldFileLock->userp,
5586                              &req, &has_insert);
5587
5588     if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5589         if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5590         force_client_lock = TRUE;
5591         }
5592         code = 0;
5593     } else if (code) {
5594         lock_ReleaseWrite(&scp->rw);
5595         return code;
5596     }
5597
5598     lock_ObtainWrite(&cm_scacheLock);
5599
5600     /* Check if we already have a sufficient server lock to allow this
5601        lock to go through. */
5602     if (IS_LOCK_WAITLOCK(oldFileLock) &&
5603         (!SERVERLOCKS_ENABLED(scp) ||
5604          scp->serverLock == oldFileLock->lockType ||
5605          scp->serverLock == LockWrite)) {
5606
5607         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5608
5609         if (SERVERLOCKS_ENABLED(scp)) {
5610             osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock.  Granting",
5611                      (int) scp->serverLock);
5612         } else {
5613             osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5614         }
5615
5616         lock_ReleaseWrite(&cm_scacheLock);
5617         lock_ReleaseWrite(&scp->rw);
5618
5619         return 0;
5620     }
5621
5622     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5623
5624         /* check if the conflicting locks have dissappeared already */
5625         for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5626
5627             fileLock = (cm_file_lock_t *)
5628                 ((char *) q - offsetof(cm_file_lock_t, fileq));
5629
5630             if (IS_LOCK_LOST(fileLock)) {
5631                 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5632                     code = CM_ERROR_BADFD;
5633                     oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5634                     osi_Log1(afsd_logp, "    found lost lock %p for same key.  Marking lock as lost",
5635                              fileLock);
5636                     break;
5637                 } else if (fileLock->lockType == LockWrite &&
5638                            INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5639                     osi_Log1(afsd_logp, "    found conflicting LOST lock %p", fileLock);
5640                     code = CM_ERROR_WOULDBLOCK;
5641                     break;
5642                 }
5643             }
5644
5645             if (IS_LOCK_ACCEPTED(fileLock) &&
5646                 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5647
5648                 if (oldFileLock->lockType != LockRead ||
5649                    fileLock->lockType != LockRead) {
5650
5651                     osi_Log1(afsd_logp, "    found conflicting lock %p", fileLock);
5652                     code = CM_ERROR_WOULDBLOCK;
5653                     break;
5654                 }
5655             }
5656         }
5657     }
5658
5659     if (code != 0) {
5660         lock_ReleaseWrite(&cm_scacheLock);
5661         lock_ReleaseWrite(&scp->rw);
5662
5663         goto handleCode;
5664     }
5665
5666     /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5667        If it is WAITUNLOCK, then we didn't find any conflicting lock
5668        but we haven't verfied whether the serverLock is sufficient to
5669        assert it.  If it is WAITLOCK, then the serverLock is
5670        insufficient to assert it. Eitherway, we are ready to accept
5671        the lock as either ACTIVE or WAITLOCK depending on the
5672        serverLock. */
5673
5674     /* First, promote the WAITUNLOCK to a WAITLOCK */
5675     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5676         if (oldFileLock->lockType == LockRead)
5677             scp->sharedLocks++;
5678         else
5679             scp->exclusiveLocks++;
5680
5681         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5682         oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5683     }
5684
5685     osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5686
5687     if (force_client_lock ||
5688         !SERVERLOCKS_ENABLED(scp) ||
5689         scp->serverLock == oldFileLock->lockType ||
5690         (oldFileLock->lockType == LockRead &&
5691          scp->serverLock == LockWrite)) {
5692
5693         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5694
5695         if ((force_client_lock ||
5696              !SERVERLOCKS_ENABLED(scp)) &&
5697             !IS_LOCK_CLIENTONLY(oldFileLock)) {
5698
5699             oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5700
5701             if (oldFileLock->lockType == LockRead)
5702                 scp->sharedLocks--;
5703             else
5704                 scp->exclusiveLocks--;
5705
5706             scp->clientLocks++;
5707         }
5708
5709         lock_ReleaseWrite(&cm_scacheLock);
5710         lock_ReleaseWrite(&scp->rw);
5711
5712         return 0;
5713
5714     } else {
5715         cm_user_t * userp;
5716
5717         code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5718                          CM_SCACHESYNC_NEEDCALLBACK
5719                          | CM_SCACHESYNC_GETSTATUS
5720                          | CM_SCACHESYNC_LOCK);
5721         if (code) {
5722             osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5723             lock_ReleaseWrite(&cm_scacheLock);
5724             goto post_syncopdone;
5725         }
5726
5727         if (!IS_LOCK_WAITLOCK(oldFileLock))
5728             goto pre_syncopdone;
5729
5730         userp = oldFileLock->userp;
5731
5732 #ifndef AGGRESSIVE_LOCKS
5733         newLock = oldFileLock->lockType;
5734 #else
5735         newLock = LockWrite;
5736 #endif
5737
5738         if (has_insert) {
5739             /* if has_insert is non-zero, then:
5740                - the lock a LockRead
5741                - we don't have permission to get a LockRead
5742                - we do have permission to get a LockWrite
5743                - the server supports VICED_CAPABILITY_WRITELOCKACL
5744             */
5745
5746             newLock = LockWrite;
5747         }
5748
5749         lock_ReleaseWrite(&cm_scacheLock);
5750
5751         /* when we get here, either we have a read-lock and want a
5752            write-lock or we don't have any locks and we want some
5753            lock. */
5754
5755         if (scp->serverLock == LockRead) {
5756
5757             osi_assertx(newLock == LockWrite, "!LockWrite");
5758
5759             osi_Log0(afsd_logp, "  Attempting to UPGRADE from LockRead to LockWrite");
5760
5761             scp->lockDataVersion = scp->dataVersion;
5762             check_data_version = TRUE;
5763
5764             code = cm_IntReleaseLock(scp, userp, &req);
5765
5766             if (code)
5767                 goto pre_syncopdone;
5768             else
5769                 scp->serverLock = -1;
5770         }
5771
5772         code = cm_IntSetLock(scp, userp, newLock, &req);
5773
5774         if (code == 0) {
5775             if (scp->dataVersion != scp->lockDataVersion) {
5776                 /* we lost a race.  too bad */
5777
5778                 osi_Log0(afsd_logp,
5779                          "  Data version mismatch while upgrading lock.");
5780                 osi_Log2(afsd_logp,
5781                          "  Data versions before=%I64d, after=%I64d",
5782                          scp->lockDataVersion,
5783                          scp->dataVersion);
5784                 osi_Log1(afsd_logp,
5785                          "  Releasing stale lock for scp 0x%x", scp);
5786
5787                 code = cm_IntReleaseLock(scp, userp, &req);
5788
5789                 scp->serverLock = -1;
5790
5791                 code = CM_ERROR_INVAL;
5792
5793                 cm_LockMarkSCacheLost(scp);
5794             } else {
5795                 scp->serverLock = newLock;
5796             }
5797         }
5798
5799     pre_syncopdone:
5800         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5801     post_syncopdone:
5802         ;
5803     }
5804
5805   handleCode:
5806     if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5807         lock_ObtainWrite(&cm_scacheLock);
5808         if (scp->fileLocksT == &oldFileLock->fileq)
5809             scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5810         osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5811         lock_ReleaseWrite(&cm_scacheLock);
5812     }
5813     lock_ReleaseWrite(&scp->rw);
5814
5815   updateLock:
5816     lock_ObtainWrite(&cm_scacheLock);
5817     if (code == 0) {
5818         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5819     } else if (code != CM_ERROR_WOULDBLOCK) {
5820         oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5821         cm_ReleaseUser(oldFileLock->userp);
5822         oldFileLock->userp = NULL;
5823         if (oldFileLock->scp) {
5824             cm_ReleaseSCacheNoLock(oldFileLock->scp);
5825             oldFileLock->scp = NULL;
5826         }
5827     }
5828     lock_ReleaseWrite(&cm_scacheLock);
5829
5830     return code;
5831 }
5832
5833 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
5834 {
5835     cm_key_t key;
5836
5837     key.process_id = process_id;
5838     key.session_id = session_id;
5839     key.file_id = file_id;
5840
5841     return key;
5842 }
5843
5844 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5845 {
5846     return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5847         ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
5848 }
5849
5850 void cm_ReleaseAllLocks(void)
5851 {
5852     cm_scache_t *scp;
5853     cm_req_t req;
5854     cm_user_t *userp;
5855     cm_key_t   key;
5856     cm_file_lock_t *fileLock;
5857     unsigned int i;
5858
5859     for (i = 0; i < cm_data.scacheHashTableSize; i++)
5860     {
5861         for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5862             while (scp->fileLocksH != NULL) {
5863                 lock_ObtainWrite(&scp->rw);
5864                 lock_ObtainWrite(&cm_scacheLock);
5865                 if (!scp->fileLocksH) {
5866                     lock_ReleaseWrite(&cm_scacheLock);
5867                     lock_ReleaseWrite(&scp->rw);
5868                     break;
5869                 }
5870                 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5871                 userp = fileLock->userp;
5872                 cm_HoldUser(userp);
5873                 key = fileLock->key;
5874                 cm_HoldSCacheNoLock(scp);
5875                 lock_ReleaseWrite(&cm_scacheLock);
5876                 cm_UnlockByKey(scp, key, 0, userp, &req);
5877                 cm_ReleaseSCache(scp);
5878                 cm_ReleaseUser(userp);
5879                 lock_ReleaseWrite(&scp->rw);
5880             }
5881         }
5882     }
5883 }