562500032b3a0fab79ad6ac51ca2803d764ff9fa
[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                     code = (&bbp->stats[0])->errorCode;
2425                     break;
2426                 default:
2427                     /* Rx and Rxkad errors are volume global */
2428                     if ( (&bbp->stats[0])->errorCode >= -64 && (&bbp->stats[0])->errorCode < 0 ||
2429                          (&bbp->stats[0])->errorCode >= ERROR_TABLE_BASE_RXK && (&bbp->stats[0])->errorCode < ERROR_TABLE_BASE_RXK + 256)
2430                         code = (&bbp->stats[0])->errorCode;
2431                 }
2432             }
2433         } while (cm_Analyze(connp, userp, reqp, &tfid, &volSync, NULL, &cbReq, code));
2434         code = cm_MapRPCError(code, reqp);
2435
2436         /*
2437          * might as well quit on an error, since we're not going to do
2438          * much better on the next immediate call, either.
2439          */
2440         if (code) {
2441             osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2442                       inlinebulk ? "Inline" : "", code);
2443             cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2444             break;
2445         }
2446
2447         /*
2448          * The bulk RPC has succeeded or at least not failed with a
2449          * volume global error result.  For items that have inlineBulk
2450          * errors we must call cm_Analyze in order to perform required
2451          * logging of errors.
2452          *
2453          * If the RPC was not inline bulk or the entry either has no error
2454          * the status must be merged.
2455          */
2456         osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2457
2458         for (i = 0; i<filesThisCall; i++) {
2459             j = filex + i;
2460             cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2461
2462             if (inlinebulk && (&bbp->stats[j])->errorCode) {
2463                 cm_req_t treq = *reqp;
2464                 cm_Analyze(NULL, userp, &treq, &tfid, &volSync, NULL, &cbReq, (&bbp->stats[j])->errorCode);
2465             } else {
2466                 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2467                 if (code != 0)
2468                     continue;
2469
2470                 /*
2471                  * otherwise, if this entry has no callback info,
2472                  * merge in this.  If there is existing callback info
2473                  * we skip the merge because the existing data must be
2474                  * current (we have a callback) and the response from
2475                  * a non-inline bulk rpc might actually be wrong.
2476                  *
2477                  * now, we have to be extra paranoid on merging in this
2478                  * information, since we didn't use cm_SyncOp before
2479                  * starting the fetch to make sure that no bad races
2480                  * were occurring.  Specifically, we need to make sure
2481                  * we don't obliterate any newer information in the
2482                  * vnode than have here.
2483                  *
2484                  * Right now, be pretty conservative: if there's a
2485                  * callback or a pending call, skip it.
2486                  * However, if the prior attempt to obtain status
2487                  * was refused access or the volume is .readonly,
2488                  * take the data in any case since we have nothing
2489                  * better for the in flight directory enumeration that
2490                  * resulted in this function being called.
2491                  */
2492                 lock_ObtainRead(&scp->rw);
2493                 if ((scp->cbServerp == NULL &&
2494                      !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2495                      (scp->flags & CM_SCACHEFLAG_PURERO) ||
2496                      (scp->flags & CM_SCACHEFLAG_EACCESS))
2497                 {
2498                     lock_ConvertRToW(&scp->rw);
2499                     cm_EndCallbackGrantingCall(scp, &cbReq,
2500                                                &bbp->callbacks[j],
2501                                                &volSync,
2502                                                CM_CALLBACK_MAINTAINCOUNT);
2503                     cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2504                     lock_ReleaseWrite(&scp->rw);
2505                 } else {
2506                     lock_ReleaseRead(&scp->rw);
2507                 }
2508                 cm_ReleaseSCache(scp);
2509             }
2510         } /* all files in the response */
2511         /* now tell it to drop the count,
2512          * after doing the vnode processing above */
2513         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2514     }   /* while there are still more files to process */
2515
2516     return code;
2517 }
2518
2519 /* called with a write locked scp and a pointer to a buffer.  Make bulk stat
2520  * calls on all undeleted files in the page of the directory specified.
2521  */
2522 afs_int32
2523 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2524                cm_req_t *reqp)
2525 {
2526     afs_int32 code;
2527     cm_bulkStat_t *bbp;
2528
2529     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2530
2531     /* should be on a buffer boundary */
2532     osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2533
2534     bbp = malloc(sizeof(cm_bulkStat_t));
2535     memset(bbp, 0, sizeof(cm_bulkStat_t));
2536     bbp->bufOffset = *offsetp;
2537
2538     lock_ReleaseWrite(&dscp->rw);
2539     /* first, assemble the file IDs we need to stat */
2540     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2541
2542     /* if we failed, bail out early */
2543     if (code && code != CM_ERROR_STOPNOW) {
2544         free(bbp);
2545         lock_ObtainWrite(&dscp->rw);
2546         return code;
2547     }
2548
2549     code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2550     osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2551
2552     lock_ObtainWrite(&dscp->rw);
2553     free(bbp);
2554     return 0;
2555 }       
2556
2557 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2558 {
2559     long mask;
2560
2561     /* initialize store back mask as inexpensive local variable */
2562     mask = 0;
2563     memset(statusp, 0, sizeof(AFSStoreStatus));
2564
2565     /* copy out queued info from scache first, if scp passed in */
2566     if (scp) {
2567         if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2568             statusp->ClientModTime = scp->clientModTime;
2569             mask |= AFS_SETMODTIME;
2570             scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2571         }
2572     }
2573
2574     if (attrp) {
2575         /* now add in our locally generated request */
2576         if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2577             statusp->ClientModTime = attrp->clientModTime;
2578             mask |= AFS_SETMODTIME;
2579         }
2580         if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2581             statusp->UnixModeBits = attrp->unixModeBits;
2582             mask |= AFS_SETMODE;
2583         }
2584         if (attrp->mask & CM_ATTRMASK_OWNER) {
2585             statusp->Owner = attrp->owner;
2586             mask |= AFS_SETOWNER;
2587         }
2588         if (attrp->mask & CM_ATTRMASK_GROUP) {
2589             statusp->Group = attrp->group;
2590             mask |= AFS_SETGROUP;
2591         }
2592     }
2593     statusp->Mask = mask;
2594 }       
2595
2596 /* set the file size, and make sure that all relevant buffers have been
2597  * truncated.  Ensure that any partially truncated buffers have been zeroed
2598  * to the end of the buffer.
2599  */
2600 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2601                    cm_req_t *reqp)
2602 {
2603     long code;
2604     int shrinking;
2605
2606     /* start by locking out buffer creation */
2607     lock_ObtainWrite(&scp->bufCreateLock);
2608
2609     /* verify that this is a file, not a dir or a symlink */
2610     lock_ObtainWrite(&scp->rw);
2611     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2612                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2613     if (code) 
2614         goto done;
2615     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2616
2617     if (scp->fileType != CM_SCACHETYPE_FILE) {
2618         code = CM_ERROR_ISDIR;
2619         goto done;
2620     }
2621
2622   startover:
2623     if (LargeIntegerLessThan(*sizep, scp->length))
2624         shrinking = 1;
2625     else
2626         shrinking = 0;
2627
2628     lock_ReleaseWrite(&scp->rw);
2629
2630     /* can't hold scp->rw lock here, since we may wait for a storeback to
2631      * finish if the buffer package is cleaning a buffer by storing it to
2632      * the server.
2633      */
2634     if (shrinking)
2635         buf_Truncate(scp, userp, reqp, sizep);
2636
2637     /* now ensure that file length is short enough, and update truncPos */
2638     lock_ObtainWrite(&scp->rw);
2639
2640     /* make sure we have a callback (so we have the right value for the
2641      * length), and wait for it to be safe to do a truncate.
2642      */
2643     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2644                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2645                       | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2646
2647     /* If we only have 'i' bits, then we should still be able to set
2648        the size of a file we created. */
2649     if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2650         code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2651                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2652                          | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2653     }
2654
2655     if (code) 
2656         goto done;
2657
2658     if (LargeIntegerLessThan(*sizep, scp->length)) {
2659         /* a real truncation.  If truncPos is not set yet, or is bigger
2660          * than where we're truncating the file, set truncPos to this
2661          * new value.
2662          */
2663         if (!shrinking)
2664             goto startover;
2665         if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2666              || LargeIntegerLessThan(*sizep, scp->length)) {
2667             /* set trunc pos */
2668             scp->truncPos = *sizep;
2669             scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2670         }
2671         /* in either case, the new file size has been changed */
2672         scp->length = *sizep;
2673         scp->mask |= CM_SCACHEMASK_LENGTH;
2674     }
2675     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2676         /* really extending the file */
2677         scp->length = *sizep;
2678         scp->mask |= CM_SCACHEMASK_LENGTH;
2679     }
2680
2681     /* done successfully */
2682     code = 0;
2683
2684     cm_SyncOpDone(scp, NULL, 
2685                    CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2686                    | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2687
2688   done:
2689     lock_ReleaseWrite(&scp->rw);
2690     lock_ReleaseWrite(&scp->bufCreateLock);
2691
2692     return code;
2693 }
2694
2695 /* set the file size or other attributes (but not both at once) */
2696 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2697                 cm_req_t *reqp)
2698 {
2699     long code;
2700     AFSFetchStatus afsOutStatus;
2701     AFSVolSync volSync;
2702     cm_conn_t *connp;
2703     AFSFid tfid;
2704     AFSStoreStatus afsInStatus;
2705     struct rx_connection * rxconnp;
2706
2707     memset(&volSync, 0, sizeof(volSync));
2708
2709     /* handle file length setting */
2710     if (attrp->mask & CM_ATTRMASK_LENGTH)
2711         return cm_SetLength(scp, &attrp->length, userp, reqp);
2712
2713     lock_ObtainWrite(&scp->rw);
2714     /* otherwise, we have to make an RPC to get the status */
2715     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2716     if (code) {
2717         lock_ReleaseWrite(&scp->rw);
2718         return code;
2719     }
2720     lock_ConvertWToR(&scp->rw);
2721
2722     /* make the attr structure */
2723     cm_StatusFromAttr(&afsInStatus, scp, attrp);
2724
2725     tfid.Volume = scp->fid.volume;
2726     tfid.Vnode = scp->fid.vnode;
2727     tfid.Unique = scp->fid.unique;
2728     lock_ReleaseRead(&scp->rw);
2729
2730     /* now make the RPC */
2731     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2732     do {
2733         code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2734         if (code) 
2735             continue;
2736
2737         rxconnp = cm_GetRxConn(connp);
2738         code = RXAFS_StoreStatus(rxconnp, &tfid,
2739                                   &afsInStatus, &afsOutStatus, &volSync);
2740         rx_PutConnection(rxconnp);
2741
2742     } while (cm_Analyze(connp, userp, reqp,
2743                          &scp->fid, &volSync, NULL, NULL, code));
2744     code = cm_MapRPCError(code, reqp);
2745
2746     if (code)
2747         osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2748     else
2749         osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2750
2751     lock_ObtainWrite(&scp->rw);
2752     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2753     if (code == 0)
2754         cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2755                         CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2756         
2757     /* if we're changing the mode bits, discard the ACL cache, 
2758      * since we changed the mode bits.
2759      */
2760     if (afsInStatus.Mask & AFS_SETMODE) 
2761         cm_FreeAllACLEnts(scp);
2762     lock_ReleaseWrite(&scp->rw);
2763     return code;
2764 }       
2765
2766 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2767                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2768 {       
2769     cm_conn_t *connp;
2770     long code;
2771     AFSFid dirAFSFid;
2772     cm_callbackRequest_t cbReq;
2773     AFSFid newAFSFid;
2774     cm_fid_t newFid;
2775     cm_scache_t *scp = NULL;
2776     int didEnd;
2777     AFSStoreStatus inStatus;
2778     AFSFetchStatus updatedDirStatus;
2779     AFSFetchStatus newFileStatus;
2780     AFSCallBack newFileCallback;
2781     AFSVolSync volSync;
2782     struct rx_connection * rxconnp;
2783     cm_dirOp_t dirop;
2784     fschar_t * fnamep = NULL;
2785
2786     memset(&volSync, 0, sizeof(volSync));
2787
2788     /* can't create names with @sys in them; must expand it manually first.
2789      * return "invalid request" if they try.
2790      */
2791     if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2792         return CM_ERROR_ATSYS;
2793     }
2794
2795 #ifdef AFS_FREELANCE_CLIENT
2796     /* Freelance root volume does not hold files */
2797     if (cm_freelanceEnabled &&
2798         dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2799         dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2800     {
2801         return CM_ERROR_NOACCESS;
2802     }
2803 #endif /* AFS_FREELANCE_CLIENT */
2804
2805     /* before starting the RPC, mark that we're changing the file data, so
2806      * that someone who does a chmod will know to wait until our call
2807      * completes.
2808      */
2809     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2810                   &dirop);
2811     lock_ObtainWrite(&dscp->rw);
2812     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2813     lock_ReleaseWrite(&dscp->rw);
2814     if (code == 0) {
2815         cm_StartCallbackGrantingCall(NULL, &cbReq);
2816     } else {
2817         cm_EndDirOp(&dirop);
2818     }
2819     if (code) {
2820         return code;
2821     }
2822     didEnd = 0;
2823
2824     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2825
2826     cm_StatusFromAttr(&inStatus, NULL, attrp);
2827
2828     /* try the RPC now */
2829     osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2830     do {
2831         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2832         if (code) 
2833             continue;
2834
2835         dirAFSFid.Volume = dscp->fid.volume;
2836         dirAFSFid.Vnode = dscp->fid.vnode;
2837         dirAFSFid.Unique = dscp->fid.unique;
2838
2839         rxconnp = cm_GetRxConn(connp);
2840         code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2841                                  &inStatus, &newAFSFid, &newFileStatus,
2842                                  &updatedDirStatus, &newFileCallback,
2843                                  &volSync);
2844         rx_PutConnection(rxconnp);
2845
2846     } while (cm_Analyze(connp, userp, reqp,
2847                          &dscp->fid, &volSync, NULL, &cbReq, code));
2848     code = cm_MapRPCError(code, reqp);
2849         
2850     if (code)
2851         osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2852     else
2853         osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2854
2855     if (dirop.scp) {
2856         lock_ObtainWrite(&dirop.scp->dirlock);
2857         dirop.lockType = CM_DIRLOCK_WRITE;
2858     }
2859     lock_ObtainWrite(&dscp->rw);
2860     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2861     if (code == 0) {
2862         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2863     }
2864     lock_ReleaseWrite(&dscp->rw);
2865
2866     /* now try to create the file's entry, too, but be careful to 
2867      * make sure that we don't merge in old info.  Since we weren't locking
2868      * out any requests during the file's creation, we may have pretty old
2869      * info.
2870      */
2871     if (code == 0) {
2872         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2873         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2874         if (code == 0) {
2875             lock_ObtainWrite(&scp->rw);
2876             scp->creator = userp;               /* remember who created it */
2877             if (!cm_HaveCallback(scp)) {
2878                 cm_EndCallbackGrantingCall(scp, &cbReq,
2879                                            &newFileCallback, &volSync, 0);
2880                 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2881                                userp, reqp, 0);
2882                 didEnd = 1;     
2883             }       
2884             lock_ReleaseWrite(&scp->rw);
2885         }
2886     }
2887
2888     /* make sure we end things properly */
2889     if (!didEnd)
2890         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2891
2892     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2893         cm_DirCreateEntry(&dirop, fnamep, &newFid);
2894 #ifdef USE_BPLUS
2895         cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2896 #endif
2897     }
2898     cm_EndDirOp(&dirop);
2899
2900     if (fnamep)
2901         free(fnamep);
2902
2903     if (scp) {
2904         if (scpp)
2905             *scpp = scp;
2906         else
2907             cm_ReleaseSCache(scp);
2908     }
2909     return code;
2910 }       
2911
2912 /*
2913  * locked if TRUE means write-locked
2914  * else the cm_scache_t rw must not be held
2915  */
2916 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
2917 {
2918     long code;
2919
2920     if (locked)
2921         lock_ReleaseWrite(&scp->rw);
2922     code = buf_CleanVnode(scp, userp, reqp);
2923     if (code == 0) {
2924         lock_ObtainWrite(&scp->rw);
2925
2926         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2927                           | CM_SCACHEMASK_CLIENTMODTIME
2928                           | CM_SCACHEMASK_LENGTH))
2929             code = cm_StoreMini(scp, userp, reqp);
2930
2931         if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2932             code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2933             scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2934         }
2935
2936         if (!locked)
2937             lock_ReleaseWrite(&scp->rw);
2938     } else if (locked) {
2939         lock_ObtainWrite(&scp->rw);
2940     }
2941     return code;
2942 }
2943
2944 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2945                 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2946 {
2947     cm_conn_t *connp;
2948     long code;
2949     AFSFid dirAFSFid;
2950     cm_callbackRequest_t cbReq;
2951     AFSFid newAFSFid;
2952     cm_fid_t newFid;
2953     cm_scache_t *scp = NULL;
2954     int didEnd;
2955     AFSStoreStatus inStatus;
2956     AFSFetchStatus updatedDirStatus;
2957     AFSFetchStatus newDirStatus;
2958     AFSCallBack newDirCallback;
2959     AFSVolSync volSync;
2960     struct rx_connection * rxconnp;
2961     cm_dirOp_t dirop;
2962     fschar_t * fnamep = NULL;
2963
2964     memset(&volSync, 0, sizeof(volSync));
2965
2966     /* can't create names with @sys in them; must expand it manually first.
2967      * return "invalid request" if they try.
2968      */
2969     if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2970         return CM_ERROR_ATSYS;
2971     }
2972
2973 #ifdef AFS_FREELANCE_CLIENT
2974     /* Freelance root volume does not hold subdirectories */
2975     if (cm_freelanceEnabled &&
2976         dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2977         dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2978     {
2979         return CM_ERROR_NOACCESS;
2980     }
2981 #endif /* AFS_FREELANCE_CLIENT */
2982
2983     /* before starting the RPC, mark that we're changing the directory
2984      * data, so that someone who does a chmod on the dir will wait until
2985      * our call completes.
2986      */
2987     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2988                   &dirop);
2989     lock_ObtainWrite(&dscp->rw);
2990     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2991     lock_ReleaseWrite(&dscp->rw);
2992     if (code == 0) {
2993         cm_StartCallbackGrantingCall(NULL, &cbReq);
2994     } else {
2995         cm_EndDirOp(&dirop);
2996     }
2997     if (code) {
2998         return code;
2999     }
3000     didEnd = 0;
3001
3002     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3003     cm_StatusFromAttr(&inStatus, NULL, attrp);
3004
3005     /* try the RPC now */
3006     osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3007     do {
3008         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3009         if (code) 
3010             continue;
3011
3012         dirAFSFid.Volume = dscp->fid.volume;
3013         dirAFSFid.Vnode = dscp->fid.vnode;
3014         dirAFSFid.Unique = dscp->fid.unique;
3015
3016         rxconnp = cm_GetRxConn(connp);
3017         code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3018                               &inStatus, &newAFSFid, &newDirStatus,
3019                               &updatedDirStatus, &newDirCallback,
3020                               &volSync);
3021         rx_PutConnection(rxconnp);
3022
3023     } while (cm_Analyze(connp, userp, reqp,
3024                         &dscp->fid, &volSync, NULL, &cbReq, code));
3025     code = cm_MapRPCError(code, reqp);
3026         
3027     if (code)
3028         osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3029     else
3030         osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3031
3032     if (dirop.scp) {
3033         lock_ObtainWrite(&dirop.scp->dirlock);
3034         dirop.lockType = CM_DIRLOCK_WRITE;
3035     }
3036     lock_ObtainWrite(&dscp->rw);
3037     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3038     if (code == 0) {
3039         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3040     }
3041     lock_ReleaseWrite(&dscp->rw);
3042
3043     /* now try to create the new dir's entry, too, but be careful to 
3044      * make sure that we don't merge in old info.  Since we weren't locking
3045      * out any requests during the file's creation, we may have pretty old
3046      * info.
3047      */
3048     if (code == 0) {
3049         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3050         code = cm_GetSCache(&newFid, &scp, userp, reqp);
3051         if (code == 0) {
3052             lock_ObtainWrite(&scp->rw);
3053             if (!cm_HaveCallback(scp)) {
3054                 cm_EndCallbackGrantingCall(scp, &cbReq,
3055                                             &newDirCallback, &volSync, 0);
3056                 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
3057                                 userp, reqp, 0);
3058                 didEnd = 1;             
3059             }
3060             lock_ReleaseWrite(&scp->rw);
3061         }
3062     }
3063
3064     /* make sure we end things properly */
3065     if (!didEnd)
3066         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3067
3068     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
3069         cm_DirCreateEntry(&dirop, fnamep, &newFid);
3070 #ifdef USE_BPLUS
3071         cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3072 #endif
3073     }
3074     cm_EndDirOp(&dirop);
3075
3076     free(fnamep);
3077
3078     if (scp) {
3079         if (scpp)
3080             *scpp = scp;
3081         else
3082             cm_ReleaseSCache(scp);
3083     }
3084
3085     /* and return error code */
3086     return code;
3087 }       
3088
3089 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3090              cm_user_t *userp, cm_req_t *reqp)
3091 {
3092     cm_conn_t *connp;
3093     long code = 0;
3094     AFSFid dirAFSFid;
3095     AFSFid existingAFSFid;
3096     AFSFetchStatus updatedDirStatus;
3097     AFSFetchStatus newLinkStatus;
3098     AFSVolSync volSync;
3099     struct rx_connection * rxconnp;
3100     cm_dirOp_t dirop;
3101     fschar_t * fnamep = NULL;
3102
3103     memset(&volSync, 0, sizeof(volSync));
3104
3105     if (dscp->fid.cell != sscp->fid.cell ||
3106         dscp->fid.volume != sscp->fid.volume) {
3107         return CM_ERROR_CROSSDEVLINK;
3108     }
3109
3110     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3111                   &dirop);
3112     lock_ObtainWrite(&dscp->rw);
3113     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3114     lock_ReleaseWrite(&dscp->rw);
3115     if (code != 0)
3116         cm_EndDirOp(&dirop);
3117
3118     if (code)
3119         return code;
3120
3121     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3122
3123     /* try the RPC now */
3124     osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3125     do {
3126         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3127         if (code) continue;
3128
3129         dirAFSFid.Volume = dscp->fid.volume;
3130         dirAFSFid.Vnode = dscp->fid.vnode;
3131         dirAFSFid.Unique = dscp->fid.unique;
3132
3133         existingAFSFid.Volume = sscp->fid.volume;
3134         existingAFSFid.Vnode = sscp->fid.vnode;
3135         existingAFSFid.Unique = sscp->fid.unique;
3136
3137         rxconnp = cm_GetRxConn(connp);
3138         code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3139             &newLinkStatus, &updatedDirStatus, &volSync);
3140         rx_PutConnection(rxconnp);
3141         osi_Log1(afsd_logp,"  RXAFS_Link returns 0x%x", code);
3142
3143     } while (cm_Analyze(connp, userp, reqp,
3144         &dscp->fid, &volSync, NULL, NULL, code));
3145
3146     code = cm_MapRPCError(code, reqp);
3147
3148     if (code)
3149         osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3150     else
3151         osi_Log0(afsd_logp, "CALL Link SUCCESS");
3152
3153     if (dirop.scp) {
3154         lock_ObtainWrite(&dirop.scp->dirlock);
3155         dirop.lockType = CM_DIRLOCK_WRITE;
3156     }
3157     lock_ObtainWrite(&dscp->rw);
3158     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3159     if (code == 0) {
3160         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3161     }
3162     lock_ReleaseWrite(&dscp->rw);
3163
3164     if (code == 0) {
3165         if (cm_CheckDirOpForSingleChange(&dirop)) {
3166             cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3167 #ifdef USE_BPLUS
3168             cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3169 #endif
3170         }
3171     }
3172     cm_EndDirOp(&dirop);
3173
3174     /* Update the linked object status */
3175     if (code == 0) {
3176         lock_ObtainWrite(&sscp->rw);
3177         cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3178         lock_ReleaseWrite(&sscp->rw);
3179     }
3180
3181     free(fnamep);
3182
3183     return code;
3184 }
3185
3186 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3187                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3188 {
3189     cm_conn_t *connp;
3190     long code;
3191     AFSFid dirAFSFid;
3192     AFSFid newAFSFid;
3193     cm_fid_t newFid;
3194     cm_scache_t *scp;
3195     AFSStoreStatus inStatus;
3196     AFSFetchStatus updatedDirStatus;
3197     AFSFetchStatus newLinkStatus;
3198     AFSVolSync volSync;
3199     struct rx_connection * rxconnp;
3200     cm_dirOp_t dirop;
3201     fschar_t *fnamep = NULL;
3202
3203     memset(&volSync, 0, sizeof(volSync));
3204
3205     /* before starting the RPC, mark that we're changing the directory data,
3206      * so that someone who does a chmod on the dir will wait until our
3207      * call completes.
3208      */
3209     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3210                   &dirop);
3211     lock_ObtainWrite(&dscp->rw);
3212     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3213     lock_ReleaseWrite(&dscp->rw);
3214     if (code != 0)
3215         cm_EndDirOp(&dirop);
3216     if (code) {
3217         return code;
3218     }
3219
3220     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3221
3222     cm_StatusFromAttr(&inStatus, NULL, attrp);
3223
3224     /* try the RPC now */
3225     osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3226     do {
3227         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3228         if (code) 
3229             continue;
3230
3231         dirAFSFid.Volume = dscp->fid.volume;
3232         dirAFSFid.Vnode = dscp->fid.vnode;
3233         dirAFSFid.Unique = dscp->fid.unique;
3234
3235         rxconnp = cm_GetRxConn(connp);
3236         code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3237                               &inStatus, &newAFSFid, &newLinkStatus,
3238                               &updatedDirStatus, &volSync);
3239         rx_PutConnection(rxconnp);
3240
3241     } while (cm_Analyze(connp, userp, reqp,
3242                          &dscp->fid, &volSync, NULL, NULL, code));
3243     code = cm_MapRPCError(code, reqp);
3244         
3245     if (code)
3246         osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3247     else
3248         osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3249
3250     if (dirop.scp) {
3251         lock_ObtainWrite(&dirop.scp->dirlock);
3252         dirop.lockType = CM_DIRLOCK_WRITE;
3253     }
3254     lock_ObtainWrite(&dscp->rw);
3255     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3256     if (code == 0) {
3257         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3258     }
3259     lock_ReleaseWrite(&dscp->rw);
3260
3261     if (code == 0) {
3262         if (cm_CheckDirOpForSingleChange(&dirop)) {
3263             cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3264
3265             cm_DirCreateEntry(&dirop, fnamep, &newFid);
3266 #ifdef USE_BPLUS
3267             cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3268 #endif
3269         }
3270     }
3271     cm_EndDirOp(&dirop);
3272
3273     /* now try to create the new dir's entry, too, but be careful to 
3274      * make sure that we don't merge in old info.  Since we weren't locking
3275      * out any requests during the file's creation, we may have pretty old
3276      * info.
3277      */
3278     if (code == 0) {
3279         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3280         code = cm_GetSCache(&newFid, &scp, userp, reqp);
3281         if (code == 0) {
3282             lock_ObtainWrite(&scp->rw);
3283             if (!cm_HaveCallback(scp)) {
3284                 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3285                                 userp, reqp, 0);
3286             }       
3287             lock_ReleaseWrite(&scp->rw);
3288             cm_ReleaseSCache(scp);
3289         }
3290     }
3291
3292     free(fnamep);
3293         
3294     /* and return error code */
3295     return code;
3296 }
3297
3298 /*! \brief Remove a directory
3299
3300   Encapsulates a call to RXAFS_RemoveDir().
3301
3302   \param[in] dscp cm_scache_t for the directory containing the
3303       directory to be removed.
3304
3305   \param[in] fnamep This will be the original name of the directory
3306       as known to the file server.   It will be passed in to RXAFS_RemoveDir().
3307       This parameter is optional.  If it is not provided the value
3308       will be looked up.
3309
3310   \param[in] cnamep Normalized name used to update the local
3311       directory caches.
3312
3313   \param[in] userp cm_user_t for the request.
3314
3315   \param[in] reqp Request tracker.
3316 */
3317 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3318 {
3319     cm_conn_t *connp;
3320     long code;
3321     AFSFid dirAFSFid;
3322     int didEnd;
3323     AFSFetchStatus updatedDirStatus;
3324     AFSVolSync volSync;
3325     struct rx_connection * rxconnp;
3326     cm_dirOp_t dirop;
3327     cm_scache_t *scp = NULL;
3328     int free_fnamep = FALSE;
3329
3330     memset(&volSync, 0, sizeof(volSync));
3331
3332     if (fnamep == NULL) {
3333         code = -1;
3334 #ifdef USE_BPLUS
3335         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
3336                              CM_DIROP_FLAG_NONE, &dirop);
3337         if (code == 0) {
3338             code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3339             if (code == 0)
3340                 free_fnamep = TRUE;
3341             cm_EndDirOp(&dirop);
3342         }
3343 #endif
3344         if (code)
3345             goto done;
3346     }
3347
3348     code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3349     if (code)
3350         goto done;
3351
3352     /* before starting the RPC, mark that we're changing the directory data,
3353      * so that someone who does a chmod on the dir will wait until our
3354      * call completes.
3355      */
3356     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3357                   &dirop);
3358     lock_ObtainWrite(&dscp->rw);
3359     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3360     lock_ReleaseWrite(&dscp->rw);
3361     if (code) {
3362         cm_EndDirOp(&dirop);
3363         goto done;
3364     }
3365     didEnd = 0;
3366
3367     /* try the RPC now */
3368     osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3369     do {
3370         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3371         if (code) 
3372             continue;
3373
3374         dirAFSFid.Volume = dscp->fid.volume;
3375         dirAFSFid.Vnode = dscp->fid.vnode;
3376         dirAFSFid.Unique = dscp->fid.unique;
3377
3378         rxconnp = cm_GetRxConn(connp);
3379         code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3380                                &updatedDirStatus, &volSync);
3381         rx_PutConnection(rxconnp);
3382
3383     } while (cm_Analyze(connp, userp, reqp,
3384                         &dscp->fid, &volSync, NULL, NULL, code));
3385     code = cm_MapRPCErrorRmdir(code, reqp);
3386
3387     if (code)
3388         osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3389     else
3390         osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3391
3392     if (dirop.scp) {
3393         lock_ObtainWrite(&dirop.scp->dirlock);
3394         dirop.lockType = CM_DIRLOCK_WRITE;
3395     }
3396     lock_ObtainWrite(&dscp->rw);
3397     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3398     if (code == 0) {
3399         cm_dnlcRemove(dscp, cnamep); 
3400         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3401     }
3402     lock_ReleaseWrite(&dscp->rw);
3403
3404     if (code == 0) {
3405         if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3406             cm_DirDeleteEntry(&dirop, fnamep);
3407 #ifdef USE_BPLUS
3408             cm_BPlusDirDeleteEntry(&dirop, cnamep);
3409 #endif
3410         }
3411     }
3412     cm_EndDirOp(&dirop);
3413
3414     if (scp) {
3415         cm_ReleaseSCache(scp);
3416         if (code == 0) {
3417             lock_ObtainWrite(&scp->rw);
3418             scp->flags |= CM_SCACHEFLAG_DELETED;
3419             lock_ReleaseWrite(&scp->rw);
3420         }
3421     }
3422
3423   done:
3424     if (free_fnamep)
3425         free(fnamep);
3426
3427     /* and return error code */
3428     return code;
3429 }
3430
3431 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3432 {
3433     /* grab mutex on contents */
3434     lock_ObtainWrite(&scp->rw);
3435
3436     /* reset the prefetch info */
3437     scp->prefetch.base.LowPart = 0;             /* base */
3438     scp->prefetch.base.HighPart = 0;
3439     scp->prefetch.end.LowPart = 0;              /* and end */
3440     scp->prefetch.end.HighPart = 0;
3441
3442     /* release mutex on contents */
3443     lock_ReleaseWrite(&scp->rw);
3444
3445     /* we're done */
3446     return 0;
3447 }       
3448
3449 /*! \brief Rename a file or directory
3450
3451   Encapsulates a RXAFS_Rename() call.
3452
3453   \param[in] oldDscp cm_scache_t for the directory containing the old
3454       name.
3455
3456   \param[in] oldNamep The original old name known to the file server.
3457       This is the name that will be passed into the RXAFS_Rename().
3458       If it is not provided, it will be looked up.
3459
3460   \param[in] normalizedOldNamep Normalized old name.  This is used for
3461   updating local directory caches.
3462
3463   \param[in] newDscp cm_scache_t for the directory containing the new
3464   name.
3465
3466   \param[in] newNamep New name. Normalized.
3467
3468   \param[in] userp cm_user_t for the request.
3469
3470   \param[in,out] reqp Request tracker.
3471
3472 */
3473 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3474                cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3475                cm_req_t *reqp)
3476 {
3477     cm_conn_t *connp;
3478     long code;
3479     AFSFid oldDirAFSFid;
3480     AFSFid newDirAFSFid;
3481     int didEnd;
3482     AFSFetchStatus updatedOldDirStatus;
3483     AFSFetchStatus updatedNewDirStatus;
3484     AFSVolSync volSync;
3485     int oneDir;
3486     struct rx_connection * rxconnp;
3487     cm_dirOp_t oldDirOp;
3488     cm_fid_t   fileFid;
3489     int        diropCode = -1;
3490     cm_dirOp_t newDirOp;
3491     fschar_t * newNamep = NULL;
3492     int free_oldNamep = FALSE;
3493     cm_scache_t *oldScp = NULL, *newScp = NULL;
3494
3495     memset(&volSync, 0, sizeof(volSync));
3496
3497     if (cOldNamep == NULL || cNewNamep == NULL ||
3498         cm_ClientStrLen(cOldNamep) == 0 ||
3499         cm_ClientStrLen(cNewNamep) == 0)
3500         return CM_ERROR_INVAL;
3501
3502     /* 
3503      * Before we permit the operation, make sure that we do not already have
3504      * an object in the destination directory that has a case-insensitive match
3505      * for this name UNLESS the matching object is the object we are renaming.
3506      */
3507     code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3508     if (code) {
3509         osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed", 
3510                  oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3511         goto done;
3512     }
3513
3514     code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3515     if (code == 0) {
3516         /* found a matching object with the new name */
3517         if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3518             /* and they don't match so return an error */
3519             osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists", 
3520                       newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3521             code = CM_ERROR_EXISTS;
3522         }
3523         cm_ReleaseSCache(newScp);
3524         newScp = NULL;
3525     } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3526         code = CM_ERROR_EXISTS;
3527     } else {
3528         code = 0;
3529     }
3530     if (code) 
3531         goto done;
3532
3533     if (oldNamep == NULL) {
3534         code = -1;
3535 #ifdef USE_BPLUS
3536         code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ,
3537                              CM_DIROP_FLAG_NONE, &oldDirOp);
3538         if (code == 0) {
3539             code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3540             if (code == 0)
3541                 free_oldNamep = TRUE;
3542             cm_EndDirOp(&oldDirOp);
3543         }
3544 #endif
3545         if (code) {
3546             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed", 
3547                       oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3548             goto done;
3549         }
3550     }
3551
3552
3553     /* before starting the RPC, mark that we're changing the directory data,
3554      * so that someone who does a chmod on the dir will wait until our call
3555      * completes.  We do this in vnode order so that we don't deadlock,
3556      * which makes the code a little verbose.
3557      */
3558     if (oldDscp == newDscp) {
3559         /* check for identical names */
3560         if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3561             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL", 
3562                       oldDscp, newDscp);
3563             code = CM_ERROR_RENAME_IDENTICAL;
3564             goto done;
3565         }
3566
3567         oneDir = 1;
3568         cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3569                       CM_DIROP_FLAG_NONE, &oldDirOp);
3570         lock_ObtainWrite(&oldDscp->rw);
3571         cm_dnlcRemove(oldDscp, cOldNamep);
3572         cm_dnlcRemove(oldDscp, cNewNamep);
3573         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3574                           CM_SCACHESYNC_STOREDATA);
3575         lock_ReleaseWrite(&oldDscp->rw);
3576         if (code != 0) {
3577             cm_EndDirOp(&oldDirOp);
3578         }
3579     }
3580     else {
3581         /* two distinct dir vnodes */
3582         oneDir = 0;
3583         if (oldDscp->fid.cell != newDscp->fid.cell ||
3584              oldDscp->fid.volume != newDscp->fid.volume) {
3585             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK", 
3586                       oldDscp, newDscp);
3587             code = CM_ERROR_CROSSDEVLINK;
3588             goto done;
3589         }
3590
3591         /* shouldn't happen that we have distinct vnodes for two
3592          * different files, but could due to deliberate attack, or
3593          * stale info.  Avoid deadlocks and quit now.
3594          */
3595         if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3596             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision", 
3597                       oldDscp, newDscp);
3598             code = CM_ERROR_CROSSDEVLINK;
3599             goto done;
3600         }
3601
3602         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3603             cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3604                           CM_DIROP_FLAG_NONE, &oldDirOp);
3605             lock_ObtainWrite(&oldDscp->rw);
3606             cm_dnlcRemove(oldDscp, cOldNamep);
3607             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3608                              CM_SCACHESYNC_STOREDATA);
3609             lock_ReleaseWrite(&oldDscp->rw);
3610             if (code != 0)
3611                 cm_EndDirOp(&oldDirOp);
3612             if (code == 0) {
3613                 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3614                               CM_DIROP_FLAG_NONE, &newDirOp);
3615                 lock_ObtainWrite(&newDscp->rw);
3616                 cm_dnlcRemove(newDscp, cNewNamep);
3617                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3618                                  CM_SCACHESYNC_STOREDATA);
3619                 lock_ReleaseWrite(&newDscp->rw);
3620                 if (code) {
3621                     cm_EndDirOp(&newDirOp);
3622
3623                     /* cleanup first one */
3624                     lock_ObtainWrite(&oldDscp->rw);
3625                     cm_SyncOpDone(oldDscp, NULL,
3626                                    CM_SCACHESYNC_STOREDATA);
3627                     lock_ReleaseWrite(&oldDscp->rw);
3628                     cm_EndDirOp(&oldDirOp);
3629                 }       
3630             }
3631         }
3632         else {
3633             /* lock the new vnode entry first */
3634             cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3635                           CM_DIROP_FLAG_NONE, &newDirOp);
3636             lock_ObtainWrite(&newDscp->rw);
3637             cm_dnlcRemove(newDscp, cNewNamep);
3638             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3639                               CM_SCACHESYNC_STOREDATA);
3640             lock_ReleaseWrite(&newDscp->rw);
3641             if (code != 0)
3642                 cm_EndDirOp(&newDirOp);
3643             if (code == 0) {
3644                 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3645                               CM_DIROP_FLAG_NONE, &oldDirOp);
3646                 lock_ObtainWrite(&oldDscp->rw);
3647                 cm_dnlcRemove(oldDscp, cOldNamep);
3648                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3649                                   CM_SCACHESYNC_STOREDATA);
3650                 lock_ReleaseWrite(&oldDscp->rw);
3651                 if (code != 0)
3652                     cm_EndDirOp(&oldDirOp);
3653                 if (code) {
3654                     /* cleanup first one */
3655                     lock_ObtainWrite(&newDscp->rw);
3656                     cm_SyncOpDone(newDscp, NULL,
3657                                    CM_SCACHESYNC_STOREDATA);
3658                     lock_ReleaseWrite(&newDscp->rw);
3659                     cm_EndDirOp(&newDirOp);
3660                 }       
3661             }
3662         }
3663     }   /* two distinct vnodes */
3664
3665     if (code) 
3666         goto done;
3667
3668     didEnd = 0;
3669
3670     newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3671
3672     /* try the RPC now */
3673     osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p", 
3674               oldDscp, newDscp);
3675     do {
3676         code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3677         if (code) 
3678             continue;
3679
3680         oldDirAFSFid.Volume = oldDscp->fid.volume;
3681         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3682         oldDirAFSFid.Unique = oldDscp->fid.unique;
3683         newDirAFSFid.Volume = newDscp->fid.volume;
3684         newDirAFSFid.Vnode = newDscp->fid.vnode;
3685         newDirAFSFid.Unique = newDscp->fid.unique;
3686
3687         rxconnp = cm_GetRxConn(connp);
3688         code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3689                             &newDirAFSFid, newNamep,
3690                             &updatedOldDirStatus, &updatedNewDirStatus,
3691                             &volSync);
3692         rx_PutConnection(rxconnp);
3693
3694     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3695                          &volSync, NULL, NULL, code));
3696     code = cm_MapRPCError(code, reqp);
3697         
3698     if (code)
3699         osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3700     else
3701         osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3702
3703     /* update the individual stat cache entries for the directories */
3704     if (oldDirOp.scp) {
3705         lock_ObtainWrite(&oldDirOp.scp->dirlock);
3706         oldDirOp.lockType = CM_DIRLOCK_WRITE;
3707     }
3708     lock_ObtainWrite(&oldDscp->rw);
3709     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3710
3711     if (code == 0)
3712         cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3713                        userp, reqp, CM_MERGEFLAG_DIROP);
3714     lock_ReleaseWrite(&oldDscp->rw);
3715
3716     if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3717 #ifdef USE_BPLUS
3718         diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3719         if (diropCode == CM_ERROR_INEXACT_MATCH)
3720             diropCode = 0;
3721         else if (diropCode == EINVAL)
3722 #endif
3723             diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3724
3725         if (diropCode == 0) {
3726             if (oneDir) {
3727                 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3728 #ifdef USE_BPLUS        
3729                 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3730 #endif
3731             }
3732                 
3733             if (diropCode == 0) { 
3734                 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3735 #ifdef USE_BPLUS
3736                 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3737 #endif  
3738             }
3739         }
3740     }
3741     cm_EndDirOp(&oldDirOp);
3742
3743     /* and update it for the new one, too, if necessary */
3744     if (!oneDir) {
3745         if (newDirOp.scp) {
3746             lock_ObtainWrite(&newDirOp.scp->dirlock);
3747             newDirOp.lockType = CM_DIRLOCK_WRITE;
3748         }
3749         lock_ObtainWrite(&newDscp->rw);
3750         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3751         if (code == 0)
3752             cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3753                             userp, reqp, CM_MERGEFLAG_DIROP);
3754         lock_ReleaseWrite(&newDscp->rw);
3755
3756 #if 0 
3757         /* 
3758          * The following optimization does not work.  
3759          * When the file server processed a RXAFS_Rename() request the 
3760          * FID of the object being moved between directories is not 
3761          * preserved.  The client does not know the new FID nor the 
3762          * version number of the target.  Not only can we not create
3763          * the directory entry in the new directory, but we can't 
3764          * preserve the cached data for the file.  It must be re-read
3765          * from the file server.  - jaltman, 2009/02/20
3766          */
3767         if (code == 0) {
3768             /* we only make the local change if we successfully made
3769                the change in the old directory AND there was only one
3770                change in the new directory */
3771             if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3772                 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3773 #ifdef USE_BPLUS
3774                 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3775 #endif
3776             }
3777         }
3778 #endif /* 0 */
3779         cm_EndDirOp(&newDirOp);
3780     }
3781
3782     /* 
3783      * After the rename the file server has invalidated the callbacks
3784      * on the file that was moved nor do we have a directory reference 
3785      * to it anymore.
3786      */
3787     lock_ObtainWrite(&oldScp->rw);
3788     cm_DiscardSCache(oldScp);
3789     lock_ReleaseWrite(&oldScp->rw);
3790
3791   done:
3792     if (oldScp)
3793         cm_ReleaseSCache(oldScp);
3794
3795     if (free_oldNamep)
3796         free(oldNamep);
3797
3798     free(newNamep);
3799
3800     /* and return error code */
3801     return code;
3802 }
3803
3804 /* Byte range locks:
3805
3806    The OpenAFS Windows client has to fake byte range locks given no
3807    server side support for such locks.  This is implemented as keyed
3808    byte range locks on the cache manager.
3809
3810    Keyed byte range locks:
3811
3812    Each cm_scache_t structure keeps track of a list of keyed locks.
3813    The key for a lock identifies an owner of a set of locks (referred
3814    to as a client).  Each key is represented by a value.  The set of
3815    key values used within a specific cm_scache_t structure form a
3816    namespace that has a scope of just that cm_scache_t structure.  The
3817    same key value can be used with another cm_scache_t structure and
3818    correspond to a completely different client.  However it is
3819    advantageous for the SMB or IFS layer to make sure that there is a
3820    1-1 mapping between client and keys over all cm_scache_t objects.
3821
3822    Assume a client C has key Key(C) (although, since the scope of the
3823    key is a cm_scache_t, the key can be Key(C,S), where S is the
3824    cm_scache_t.  But assume a 1-1 relation between keys and clients).
3825    A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3826    inclusive (a.k.a. [O,O+L-1]).  The function Key(x) is implemented
3827    through cm_generateKey() function for both SMB and IFS.
3828
3829    The list of locks for a cm_scache_t object S is maintained in
3830    S->fileLocks.  The cache manager will set a lock on the AFS file
3831    server in order to assert the locks in S->fileLocks.  If only
3832    shared locks are in place for S, then the cache manager will obtain
3833    a LockRead lock, while if there are any exclusive locks, it will
3834    obtain a LockWrite lock.  If the exclusive locks are all released
3835    while the shared locks remain, then the cache manager will
3836    downgrade the lock from LockWrite to LockRead.  Similarly, if an
3837    exclusive lock is obtained when only shared locks exist, then the
3838    cache manager will try to upgrade the lock from LockRead to
3839    LockWrite.
3840
3841    Each lock L owned by client C maintains a key L->key such that
3842    L->key == Key(C), the effective range defined by L->LOffset and
3843    L->LLength such that the range of bytes affected by the lock is
3844    (L->LOffset, +L->LLength), a type maintained in L->LockType which
3845    is either exclusive or shared.
3846
3847    Lock states:
3848
3849    A lock exists iff it is in S->fileLocks for some cm_scache_t
3850    S. Existing locks are in one of the following states: ACTIVE,
3851    WAITLOCK, WAITUNLOCK, LOST, DELETED.
3852
3853    The following sections describe each lock and the associated
3854    transitions.
3855
3856    1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3857       the lock with the AFS file server.  This type of lock can be
3858       exercised by a client to read or write to the locked region (as
3859       the lock allows).
3860
3861       1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3862         server lock that was required to assert the lock.  Before
3863         marking the lock as lost, the cache manager checks if the file
3864         has changed on the server.  If the file has not changed, then
3865         the cache manager will attempt to obtain a new server lock
3866         that is sufficient to assert the client side locks for the
3867         file.  If any of these fail, the lock is marked as LOST.
3868         Otherwise, it is left as ACTIVE.
3869
3870       1.2 ACTIVE->DELETED: Lock is released.
3871
3872    2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3873       grants the lock but the lock is yet to be asserted with the AFS
3874       file server.  Once the file server grants the lock, the state
3875       will transition to an ACTIVE lock.
3876
3877       2.1 WAITLOCK->ACTIVE: The server granted the lock.
3878
3879       2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3880         waiting.
3881
3882       2.3 WAITLOCK->LOST: One or more locks from this client were
3883         marked as LOST.  No further locks will be granted to this
3884         client until all lost locks are removed.
3885
3886    3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3887       receives a request for a lock that conflicts with an existing
3888       ACTIVE or WAITLOCK lock.  The lock will be placed in the queue
3889       and will be granted at such time the conflicting locks are
3890       removed, at which point the state will transition to either
3891       WAITLOCK or ACTIVE.
3892
3893       3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed.  The
3894         current serverLock is sufficient to assert this lock, or a
3895         sufficient serverLock is obtained.
3896
3897       3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3898         however the required serverLock is yet to be asserted with the
3899         server.
3900
3901       3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3902         released.
3903
3904       3.5 WAITUNLOCK->LOST: One or more locks from this client were
3905         marked as LOST.  No further locks will be granted to this
3906         client until all lost locks are removed.
3907
3908    4. LOST: A lock L is LOST if the server lock that was required to
3909       assert the lock could not be obtained or if it could not be
3910       extended, or if other locks by the same client were LOST.
3911       Essentially, once a lock is LOST, the contract between the cache
3912       manager and that specific client is no longer valid.
3913
3914       The cache manager rechecks the server lock once every minute and
3915       extends it as appropriate.  If this is not done for 5 minutes,
3916       the AFS file server will release the lock (the 5 minute timeout
3917       is based on current file server code and is fairly arbitrary).
3918       Once released, the lock cannot be re-obtained without verifying
3919       that the contents of the file hasn't been modified since the
3920       time the lock was released.  Re-obtaining the lock without
3921       verifying this may lead to data corruption.  If the lock can not
3922       be obtained safely, then all active locks for the cm_scache_t
3923       are marked as LOST.
3924
3925       4.1 LOST->DELETED: The lock is released.
3926
3927    5. DELETED: The lock is no longer relevant.  Eventually, it will
3928       get removed from the cm_scache_t. In the meantime, it will be
3929       treated as if it does not exist.
3930
3931       5.1 DELETED->not exist: The lock is removed from the
3932         cm_scache_t.
3933
3934    The following are classifications of locks based on their state.
3935
3936    6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK.  These locks
3937       have been accepted by the cache manager, but may or may not have
3938       been granted back to the client.
3939
3940    7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3941
3942    8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3943
3944    Lock operation:
3945
3946    A client C can READ range (Offset,+Length) of a file represented by
3947    cm_scache_t S iff (1):
3948
3949    1. for all _a_ in (Offset,+Length), all of the following is true:
3950
3951        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3952          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3953          shared.
3954
3955        1.2 For each LOST lock L in S->fileLocks such that _a_ in
3956          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3957          Key(C)
3958
3959        (When locks are lost on an cm_scache_t, all locks are lost.  By
3960        4.2 (below), if there is an exclusive LOST lock, then there
3961        can't be any overlapping ACTIVE locks.)
3962
3963    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3964
3965    2. for all _a_ in (Offset,+Length), one of the following is true:
3966
3967        2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3968          does not exist a LOST lock L such that _a_ in
3969          (L->LOffset,+L->LLength).
3970
3971        2.2 Byte _a_ of S is owned by C under lock L (as specified in
3972          1.2) AND L->LockType is exclusive.
3973
3974    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3975
3976    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3977       true:
3978
3979        3.1 If L->LockType is exclusive then there does NOT exist a
3980          ACCEPTED lock M in S->fileLocks such that _a_ in
3981          (M->LOffset,+M->LLength).
3982
3983          (If we count all QUEUED locks then we hit cases such as
3984          cascading waiting locks where the locks later on in the queue
3985          can be granted without compromising file integrity.  On the
3986          other hand if only ACCEPTED locks are considered, then locks
3987          that were received earlier may end up waiting for locks that
3988          were received later to be unlocked. The choice of ACCEPTED
3989          locks was made to mimic the Windows byte range lock
3990          semantics.)
3991
3992        3.2 If L->LockType is shared then for each ACCEPTED lock M in
3993          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3994          M->LockType is shared.
3995
3996    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3997
3998        4.1 M->key != Key(C)
3999
4000        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4001          and (M->LOffset,+M->LLength) do not intersect.
4002
4003          (Note: If a client loses a lock, it loses all locks.
4004          Subsequently, it will not be allowed to obtain any more locks
4005          until all existing LOST locks that belong to the client are
4006          released.  Once all locks are released by a single client,
4007          there exists no further contract between the client and AFS
4008          about the contents of the file, hence the client can then
4009          proceed to obtain new locks and establish a new contract.
4010
4011          This doesn't quite work as you think it should, because most
4012          applications aren't built to deal with losing locks they
4013          thought they once had.  For now, we don't have a good
4014          solution to lost locks.
4015
4016          Also, for consistency reasons, we have to hold off on
4017          granting locks that overlap exclusive LOST locks.)
4018
4019    A client C can only unlock locks L in S->fileLocks which have
4020    L->key == Key(C).
4021
4022    The representation and invariants are as follows:
4023
4024    - Each cm_scache_t structure keeps:
4025
4026        - A queue of byte-range locks (cm_scache_t::fileLocks) which
4027          are of type cm_file_lock_t.
4028
4029        - A record of the highest server-side lock that has been
4030          obtained for this object (cm_scache_t::serverLock), which is
4031          one of (-1), LockRead, LockWrite.
4032
4033        - A count of ACCEPTED exclusive and shared locks that are in the
4034          queue (cm_scache_t::sharedLocks and
4035          cm_scache_t::exclusiveLocks)
4036
4037    - Each cm_file_lock_t structure keeps:
4038
4039        - The type of lock (cm_file_lock_t::LockType)
4040
4041        - The key associated with the lock (cm_file_lock_t::key)
4042
4043        - The offset and length of the lock (cm_file_lock_t::LOffset
4044          and cm_file_lock_t::LLength)
4045
4046        - The state of the lock.
4047
4048        - Time of issuance or last successful extension
4049
4050    Semantic invariants:
4051
4052        I1. The number of ACCEPTED locks in S->fileLocks are
4053            (S->sharedLocks + S->exclusiveLocks)
4054
4055    External invariants:
4056
4057        I3. S->serverLock is the lock that we have asserted with the
4058            AFS file server for this cm_scache_t.
4059
4060        I4. S->serverLock == LockRead iff there is at least one ACTIVE
4061            shared lock, but no ACTIVE exclusive locks.
4062
4063        I5. S->serverLock == LockWrite iff there is at least one ACTIVE
4064            exclusive lock.
4065
4066        I6. If L is a LOST lock, then for each lock M in S->fileLocks,
4067            M->key == L->key IMPLIES M is LOST or DELETED.
4068
4069    --asanka
4070  */
4071
4072 #define IS_LOCK_ACTIVE(lockp)     (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
4073
4074 #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)
4075
4076 #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)
4077
4078 #define IS_LOCK_LOST(lockp)       (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
4079
4080 #define IS_LOCK_DELETED(lockp)    (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
4081
4082 /* unsafe */
4083 #define IS_LOCK_ACCEPTED(lockp)   (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
4084
4085 /* unsafe */
4086 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
4087
4088 /* unsafe */
4089 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
4090
4091 /* unsafe */
4092 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
4093
4094 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
4095 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
4096 #else
4097 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
4098 #endif
4099
4100 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
4101
4102 #if defined(VICED_CAPABILITY_WRITELOCKACL)
4103 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
4104 #else
4105 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
4106
4107 /* This should really be defined in any build that this code is being
4108    compiled. */
4109 #error  VICED_CAPABILITY_WRITELOCKACL not defined.
4110 #endif
4111
4112 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
4113 {
4114     afs_int64 int_begin;
4115     afs_int64 int_end;
4116
4117     int_begin = MAX(pos->offset, neg->offset);
4118     int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
4119
4120     if (int_begin < int_end) {
4121         if (int_begin == pos->offset) {
4122             pos->length = pos->offset + pos->length - int_end;
4123             pos->offset = int_end;
4124         } else if (int_end == pos->offset + pos->length) {
4125             pos->length = int_begin - pos->offset;
4126         }
4127
4128         /* We only subtract ranges if the resulting range is
4129            contiguous.  If we try to support non-contigous ranges, we
4130            aren't actually improving performance. */
4131     }
4132 }
4133
4134 /* Called with scp->rw held.  Returns 0 if all is clear to read the
4135    specified range by the client identified by key.
4136  */
4137 long cm_LockCheckRead(cm_scache_t *scp, 
4138                       LARGE_INTEGER LOffset, 
4139                       LARGE_INTEGER LLength, 
4140                       cm_key_t key)
4141 {
4142 #ifndef ADVISORY_LOCKS
4143
4144     cm_file_lock_t *fileLock;
4145     osi_queue_t *q;
4146     long code = 0;
4147     cm_range_t range;
4148     int substract_ranges = FALSE;
4149
4150     range.offset = LOffset.QuadPart;
4151     range.length = LLength.QuadPart;
4152
4153     /*
4154
4155      1. for all _a_ in (Offset,+Length), all of the following is true:
4156
4157        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4158          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4159          shared.
4160
4161        1.2 For each LOST lock L in S->fileLocks such that _a_ in
4162          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4163          Key(C)
4164
4165     */
4166
4167     lock_ObtainRead(&cm_scacheLock);
4168
4169     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4170         fileLock = 
4171             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4172
4173         if (INTERSECT_RANGE(range, fileLock->range)) {
4174             if (IS_LOCK_ACTIVE(fileLock)) {
4175                 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4176
4177                     /* If there is an active lock for this client, it
4178                        is safe to substract ranges.*/
4179                     cm_LockRangeSubtract(&range, &fileLock->range);
4180                     substract_ranges = TRUE;
4181                 } else {
4182                     if (fileLock->lockType != LockRead) {
4183                         code = CM_ERROR_LOCK_CONFLICT;
4184                         break;
4185                     }
4186
4187                     /* even if the entire range is locked for reading,
4188                        we still can't grant the lock at this point
4189                        because the client may have lost locks. That
4190                        is, unless we have already seen an active lock
4191                        belonging to the client, in which case there
4192                        can't be any lost locks for this client. */
4193                     if (substract_ranges)
4194                         cm_LockRangeSubtract(&range, &fileLock->range);
4195                 }
4196             } else if (IS_LOCK_LOST(fileLock) &&
4197                        (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4198                 code = CM_ERROR_BADFD;
4199                 break;
4200             }
4201         }
4202     }
4203
4204     lock_ReleaseRead(&cm_scacheLock);
4205
4206     osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4207               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4208
4209     return code;
4210
4211 #else
4212
4213     return 0;
4214
4215 #endif
4216 }
4217
4218 /* Called with scp->rw held.  Returns 0 if all is clear to write the
4219    specified range by the client identified by key.
4220  */
4221 long cm_LockCheckWrite(cm_scache_t *scp,
4222                        LARGE_INTEGER LOffset,
4223                        LARGE_INTEGER LLength,
4224                        cm_key_t key)
4225 {
4226 #ifndef ADVISORY_LOCKS
4227
4228     cm_file_lock_t *fileLock;
4229     osi_queue_t *q;
4230     long code = 0;
4231     cm_range_t range;
4232
4233     range.offset = LOffset.QuadPart;
4234     range.length = LLength.QuadPart;
4235
4236     /*
4237    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4238
4239    2. for all _a_ in (Offset,+Length), one of the following is true:
4240
4241        2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4242          lock L such that _a_ in (L->LOffset,+L->LLength).
4243
4244        2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4245          exclusive.
4246     */
4247
4248     lock_ObtainRead(&cm_scacheLock);
4249
4250     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4251         fileLock = 
4252             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4253
4254         if (INTERSECT_RANGE(range, fileLock->range)) {
4255             if (IS_LOCK_ACTIVE(fileLock)) {
4256                 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4257                     if (fileLock->lockType == LockWrite) {
4258
4259                         /* if there is an active lock for this client, it
4260                            is safe to substract ranges */
4261                         cm_LockRangeSubtract(&range, &fileLock->range);
4262                     } else {
4263                         code = CM_ERROR_LOCK_CONFLICT;
4264                         break;
4265                     }
4266                 } else {
4267                     code = CM_ERROR_LOCK_CONFLICT;
4268                     break;
4269                 }
4270             } else if (IS_LOCK_LOST(fileLock)) {
4271                 code = CM_ERROR_BADFD;
4272                 break;
4273             }
4274         }
4275     }
4276
4277     lock_ReleaseRead(&cm_scacheLock);
4278
4279     osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4280               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4281
4282     return code;
4283
4284 #else
4285
4286     return 0;
4287
4288 #endif
4289 }
4290
4291 /* Called with cm_scacheLock write locked */
4292 static cm_file_lock_t * cm_GetFileLock(void) {
4293     cm_file_lock_t * l;
4294
4295     l = (cm_file_lock_t *) cm_freeFileLocks;
4296     if (l) {
4297         osi_QRemove(&cm_freeFileLocks, &l->q);
4298     } else {
4299         l = malloc(sizeof(cm_file_lock_t));
4300         osi_assertx(l, "null cm_file_lock_t");
4301     }
4302
4303     memset(l, 0, sizeof(cm_file_lock_t));
4304
4305     return l;
4306 }
4307
4308 /* Called with cm_scacheLock write locked */
4309 static void cm_PutFileLock(cm_file_lock_t *l) {
4310     osi_QAdd(&cm_freeFileLocks, &l->q);
4311 }
4312
4313 /* called with scp->rw held.  May release it during processing, but
4314    leaves it held on exit. */
4315 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4316                    cm_req_t * reqp) {
4317     long code = 0;
4318     AFSFid tfid;
4319     cm_fid_t cfid;
4320     cm_conn_t * connp;
4321     struct rx_connection * rxconnp;
4322     AFSVolSync volSync;
4323     afs_uint32 reqflags = reqp->flags;
4324
4325     memset(&volSync, 0, sizeof(volSync));
4326
4327     tfid.Volume = scp->fid.volume;
4328     tfid.Vnode = scp->fid.vnode;
4329     tfid.Unique = scp->fid.unique;
4330     cfid = scp->fid;
4331
4332     osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4333
4334     reqp->flags |= CM_REQ_NORETRY;
4335     lock_ReleaseWrite(&scp->rw);
4336
4337     do {
4338         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4339         if (code) 
4340             break;
4341
4342         rxconnp = cm_GetRxConn(connp);
4343         code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4344                              &volSync);
4345         rx_PutConnection(rxconnp);
4346
4347     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4348                         NULL, NULL, code));
4349
4350     code = cm_MapRPCError(code, reqp);
4351     if (code) {
4352         osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4353     } else {
4354         osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4355     }
4356
4357     lock_ObtainWrite(&scp->rw);
4358     reqp->flags = reqflags;
4359     return code;
4360 }
4361
4362 /* called with scp->rw held.  Releases it during processing */
4363 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4364                        cm_req_t * reqp) {
4365     long code = 0;
4366     AFSFid tfid;
4367     cm_fid_t cfid;
4368     cm_conn_t * connp;
4369     struct rx_connection * rxconnp;
4370     AFSVolSync volSync;
4371
4372     memset(&volSync, 0, sizeof(volSync));
4373
4374     tfid.Volume = scp->fid.volume;
4375     tfid.Vnode = scp->fid.vnode;
4376     tfid.Unique = scp->fid.unique;
4377     cfid = scp->fid;
4378
4379     lock_ReleaseWrite(&scp->rw);
4380
4381     osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4382
4383     do {
4384         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4385         if (code) 
4386             break;
4387
4388         rxconnp = cm_GetRxConn(connp);
4389         code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4390         rx_PutConnection(rxconnp);
4391
4392     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4393                         NULL, NULL, code));
4394     code = cm_MapRPCError(code, reqp);
4395     if (code)
4396         osi_Log1(afsd_logp,
4397                  "CALL ReleaseLock FAILURE, code 0x%x", code);
4398     else
4399         osi_Log0(afsd_logp,
4400                  "CALL ReleaseLock SUCCESS");
4401         
4402     lock_ObtainWrite(&scp->rw);
4403
4404     return code;
4405 }
4406
4407 /* called with scp->rw held.  May release it during processing, but
4408    will exit with lock held.
4409
4410    This will return:
4411
4412    - 0 if the user has permission to get the specified lock for the scp
4413
4414    - CM_ERROR_NOACCESS if not
4415
4416    Any other error from cm_SyncOp will be sent down untranslated.
4417
4418    If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4419    phas_insert (if non-NULL) will receive a boolean value indicating
4420    whether the user has INSERT permission or not.
4421 */
4422 long cm_LockCheckPerms(cm_scache_t * scp,
4423                        int lock_type,
4424                        cm_user_t * userp,
4425                        cm_req_t * reqp,
4426                        int * phas_insert)
4427 {
4428     long rights = 0;
4429     long code = 0, code2 = 0;
4430
4431     /* lock permissions are slightly tricky because of the 'i' bit.
4432        If the user has PRSFS_LOCK, she can read-lock the file.  If the
4433        user has PRSFS_WRITE, she can write-lock the file.  However, if
4434        the user has PRSFS_INSERT, then she can write-lock new files,
4435        but not old ones.  Since we don't have information about
4436        whether a file is new or not, we assume that if the user owns
4437        the scp, then she has the permissions that are granted by
4438        PRSFS_INSERT. */
4439
4440     osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4441              scp, lock_type, userp);
4442
4443     if (lock_type == LockRead)
4444         rights |= PRSFS_LOCK;
4445     else if (lock_type == LockWrite)
4446         rights |= PRSFS_WRITE | PRSFS_LOCK;
4447     else {
4448         /* hmmkay */
4449         osi_assertx(FALSE, "invalid lock type");
4450         return 0;
4451     }
4452
4453     if (phas_insert)
4454         *phas_insert = FALSE;
4455
4456     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4457                      CM_SCACHESYNC_GETSTATUS |
4458                      CM_SCACHESYNC_NEEDCALLBACK);
4459
4460     if (phas_insert && scp->creator == userp) {
4461
4462         /* If this file was created by the user, then we check for
4463            PRSFS_INSERT.  If the file server is recent enough, then
4464            this should be sufficient for her to get a write-lock (but
4465            not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4466            indicates whether a file server supports getting write
4467            locks when the user only has PRSFS_INSERT. 
4468            
4469            If the file was not created by the user we skip the check
4470            because the INSERT bit will not apply to this user even
4471            if it is set.
4472          */
4473
4474         code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4475                          CM_SCACHESYNC_GETSTATUS |
4476                          CM_SCACHESYNC_NEEDCALLBACK);
4477
4478         if (code2 == CM_ERROR_NOACCESS) {
4479             osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4480         } else {
4481             *phas_insert = TRUE;
4482             osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4483         }
4484     }
4485
4486     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4487
4488     osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4489
4490     return code;
4491 }
4492
4493 /* called with scp->rw held */
4494 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4495              LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4496              cm_key_t key,
4497              int allowWait, cm_user_t *userp, cm_req_t *reqp,
4498              cm_file_lock_t **lockpp)
4499 {
4500     long code = 0;
4501     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4502     cm_file_lock_t *fileLock;
4503     osi_queue_t *q;
4504     cm_range_t range;
4505     int wait_unlock = FALSE;
4506     int force_client_lock = FALSE;
4507
4508     osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4509              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4510     osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait, 
4511              key.process_id, key.session_id, key.file_id);
4512
4513     /*
4514    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4515
4516    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4517       true:
4518
4519        3.1 If L->LockType is exclusive then there does NOT exist a
4520          ACCEPTED lock M in S->fileLocks such that _a_ in
4521          (M->LOffset,+M->LLength).
4522
4523        3.2 If L->LockType is shared then for each ACCEPTED lock M in
4524          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4525          M->LockType is shared.
4526
4527    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4528
4529        4.1 M->key != Key(C)
4530
4531        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4532          and (M->LOffset,+M->LLength) do not intersect.
4533     */
4534
4535     range.offset = LOffset.QuadPart;
4536     range.length = LLength.QuadPart;
4537
4538     lock_ObtainRead(&cm_scacheLock);
4539
4540     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4541         fileLock =
4542             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4543
4544         if (IS_LOCK_LOST(fileLock)) {
4545             if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4546                 code = CM_ERROR_BADFD;
4547                 break;
4548             } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4549                 code = CM_ERROR_WOULDBLOCK;
4550                 wait_unlock = TRUE;
4551                 break;
4552             }
4553         }
4554
4555         /* we don't need to check for deleted locks here since deleted
4556            locks are dequeued from scp->fileLocks */
4557         if (IS_LOCK_ACCEPTED(fileLock) &&
4558            INTERSECT_RANGE(range, fileLock->range)) {
4559
4560             if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4561                 fileLock->lockType != LockRead) {
4562                 wait_unlock = TRUE;
4563                 code = CM_ERROR_WOULDBLOCK;
4564                 break;
4565             }
4566         }
4567     }
4568
4569     lock_ReleaseRead(&cm_scacheLock);
4570
4571     if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4572         if (Which == scp->serverLock ||
4573            (Which == LockRead && scp->serverLock == LockWrite)) {
4574
4575             int has_insert = 0;
4576
4577             /* we already have the lock we need */
4578             osi_Log3(afsd_logp, "   we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]", 
4579                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4580
4581             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4582
4583             /* special case: if we don't have permission to read-lock
4584                the file, then we force a clientside lock.  This is to
4585                compensate for applications that obtain a read-lock for
4586                reading files off of directories that don't grant
4587                read-locks to the user. */
4588             if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4589
4590                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4591                     osi_Log0(afsd_logp, "   User has no read-lock perms, but has INSERT perms.");
4592                     code = 0;
4593                 } else {
4594                     osi_Log0(afsd_logp, "   User has no read-lock perms. Forcing client-side lock");
4595                     force_client_lock = TRUE;
4596                 }
4597             }
4598
4599         } else if ((scp->exclusiveLocks > 0) ||
4600                    (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4601             int has_insert = 0;
4602
4603             /* We are already waiting for some other lock.  We should
4604                wait for the daemon to catch up instead of generating a
4605                flood of SetLock calls. */
4606             osi_Log3(afsd_logp, "   already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4607                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4608
4609             /* see if we have permission to create the lock in the
4610                first place. */
4611             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4612             if (code == 0)
4613                 code = CM_ERROR_WOULDBLOCK;
4614             else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4615
4616                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4617                     osi_Log0(afsd_logp,
4618                              "   User has no read-lock perms, but has INSERT perms.");
4619                     code = CM_ERROR_WOULDBLOCK;
4620                 } else {
4621                     osi_Log0(afsd_logp,
4622                              "   User has no read-lock perms. Forcing client-side lock");
4623                     force_client_lock = TRUE;
4624                 }
4625             }
4626
4627             /* leave any other codes as-is */
4628
4629         } else {
4630             int newLock;
4631             int check_data_version = FALSE;
4632             int has_insert = 0;
4633
4634             /* first check if we have permission to elevate or obtain
4635                the lock. */
4636             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4637             if (code) {
4638                 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4639                     (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4640                     osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
4641                     force_client_lock = TRUE;
4642                 }
4643                 goto check_code;
4644             }
4645
4646             /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4647
4648             if (scp->serverLock == LockRead && Which == LockWrite) {
4649
4650                 /* We want to escalate the lock to a LockWrite.
4651                  * Unfortunately that's not really possible without
4652                  * letting go of the current lock.  But for now we do
4653                  * it anyway. */
4654
4655                 osi_Log0(afsd_logp,
4656                          "   attempting to UPGRADE from LockRead to LockWrite.");
4657                 osi_Log1(afsd_logp,
4658                          "   dataVersion on scp: %I64d", scp->dataVersion);
4659
4660                 /* we assume at this point (because scp->serverLock
4661                    was valid) that we had a valid server lock. */
4662                 scp->lockDataVersion = scp->dataVersion;
4663                 check_data_version = TRUE;
4664         
4665                 code = cm_IntReleaseLock(scp, userp, reqp);
4666
4667                 if (code) {
4668                     /* We couldn't release the lock */
4669                     goto check_code;
4670                 } else {
4671                     scp->serverLock = -1;
4672                 }
4673             }
4674
4675             /* We need to obtain a server lock of type Which in order
4676              * to assert this file lock */
4677 #ifndef AGGRESSIVE_LOCKS
4678             newLock = Which;
4679 #else
4680             newLock = LockWrite;
4681 #endif
4682
4683             code = cm_IntSetLock(scp, userp, newLock, reqp);
4684
4685 #ifdef AGGRESSIVE_LOCKS
4686             if ((code == CM_ERROR_WOULDBLOCK ||
4687                  code == CM_ERROR_NOACCESS) && newLock != Which) {
4688                 /* we wanted LockRead.  We tried LockWrite. Now try
4689                  * LockRead again */
4690                 newLock = Which;
4691
4692                 /* am I sane? */
4693                 osi_assertx(newLock == LockRead, "lock type not read");
4694
4695                 code = cm_IntSetLock(scp, userp, newLock, reqp);
4696             }
4697 #endif
4698
4699             if (code == CM_ERROR_NOACCESS) {
4700                 if (Which == LockRead) {
4701                     if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4702                         long tcode;
4703                         /* We requested a read-lock, but we have permission to
4704                          * get a write-lock. Try that */
4705
4706                         tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4707
4708                         if (tcode == 0) {
4709                             newLock = LockWrite;
4710
4711                             osi_Log0(afsd_logp, "   User has 'i' perms and the request was for a LockRead.  Trying to get a LockWrite instead");
4712
4713                             code = cm_IntSetLock(scp, userp, newLock, reqp);
4714                         }
4715                     } else {
4716                         osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
4717                         force_client_lock = TRUE;
4718                     }
4719                 } else if (Which == LockWrite &&
4720                            scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4721                     long tcode;
4722
4723                     /* Special case: if the lock request was for a
4724                      * LockWrite and the user owns the file and we weren't
4725                      * allowed to obtain the serverlock, we either lost a
4726                      * race (the permissions changed from under us), or we
4727                      * have 'i' bits, but we aren't allowed to lock the
4728                      * file. */
4729
4730                     /* check if we lost a race... */
4731                     tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4732
4733                     if (tcode == 0) {
4734                         osi_Log0(afsd_logp, "   User has 'i' perms but can't obtain write locks. Using client-side locks.");
4735                         force_client_lock = TRUE;
4736                     }
4737                 }
4738             }
4739
4740             if (code == 0 && check_data_version &&
4741                scp->dataVersion != scp->lockDataVersion) {
4742                 /* We lost a race.  Although we successfully obtained
4743                  * a lock, someone modified the file in between.  The
4744                  * locks have all been technically lost. */
4745
4746                 osi_Log0(afsd_logp,
4747                          "  Data version mismatch while upgrading lock.");
4748                 osi_Log2(afsd_logp,
4749                          "  Data versions before=%I64d, after=%I64d",
4750                          scp->lockDataVersion,
4751                          scp->dataVersion);
4752                 osi_Log1(afsd_logp,
4753                          "  Releasing stale lock for scp 0x%x", scp);
4754
4755                 code = cm_IntReleaseLock(scp, userp, reqp);
4756
4757                 scp->serverLock = -1;
4758
4759                 code = CM_ERROR_INVAL;
4760             } else if (code == 0) {
4761                 scp->serverLock = newLock;
4762                 scp->lockDataVersion = scp->dataVersion;
4763             }
4764
4765             if (code != 0 &&
4766                 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4767                 scp->serverLock == -1) {
4768                 /* Oops. We lost the lock. */
4769                 cm_LockMarkSCacheLost(scp);
4770             }
4771         }
4772     } else if (code == 0) {     /* server locks not enabled */
4773         osi_Log0(afsd_logp,
4774                  "  Skipping server lock for scp");
4775     }
4776
4777  check_code:
4778
4779     if (code != 0 && !force_client_lock) {
4780         /* Special case error translations
4781
4782            Applications don't expect certain errors from a
4783            LockFile/UnlockFile call.  We need to translate some error
4784            code to codes that apps expect and handle. */
4785
4786         /* We shouldn't actually need to handle this case since we
4787            simulate locks for RO scps anyway. */
4788         if (code == CM_ERROR_READONLY) {
4789             osi_Log0(afsd_logp, "   Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4790             code = CM_ERROR_NOACCESS;
4791         }
4792     }
4793
4794     if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4795         force_client_lock) {
4796
4797         /* clear the error if we are forcing a client lock, so we
4798            don't get confused later. */
4799         if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4800             code = 0;
4801
4802         lock_ObtainWrite(&cm_scacheLock);
4803         fileLock = cm_GetFileLock();
4804         lock_ReleaseWrite(&cm_scacheLock);
4805 #ifdef DEBUG
4806         fileLock->fid = scp->fid;
4807 #endif
4808         fileLock->key = key;
4809         fileLock->lockType = Which;
4810         cm_HoldUser(userp);
4811         fileLock->userp = userp;
4812         fileLock->range = range;
4813         fileLock->flags = (code == 0 ? 0 : 
4814                            ((wait_unlock)?
4815                             CM_FILELOCK_FLAG_WAITUNLOCK :
4816                             CM_FILELOCK_FLAG_WAITLOCK));
4817
4818         if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4819             fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4820
4821         fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4822
4823         lock_ObtainWrite(&cm_scacheLock);
4824         osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4825         cm_HoldSCacheNoLock(scp);
4826         fileLock->scp = scp;
4827         osi_QAdd(&cm_allFileLocks, &fileLock->q);
4828         lock_ReleaseWrite(&cm_scacheLock);
4829
4830         if (code != 0) {
4831             *lockpp = fileLock;
4832         }
4833
4834         if (IS_LOCK_CLIENTONLY(fileLock)) {
4835             scp->clientLocks++;
4836         } else if (IS_LOCK_ACCEPTED(fileLock)) {
4837             if (Which == LockRead)
4838                 scp->sharedLocks++;
4839             else
4840                 scp->exclusiveLocks++;
4841         }
4842
4843         osi_Log3(afsd_logp,
4844                  "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4845                  fileLock, fileLock->flags, scp);
4846         osi_Log4(afsd_logp,
4847                  "   exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4848                  scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4849                  (int)(signed char) scp->serverLock);
4850     } else {
4851         osi_Log1(afsd_logp,
4852                  "cm_Lock Rejecting lock (code = 0x%x)", code);
4853     }
4854
4855     /* Convert from would block to lock not granted */
4856     if (code == CM_ERROR_WOULDBLOCK)
4857         code = CM_ERROR_LOCK_NOT_GRANTED;
4858
4859     return code;
4860 }
4861
4862 /* Called with scp->rw held */
4863 long cm_UnlockByKey(cm_scache_t * scp,
4864                     cm_key_t key,
4865                     int flags,
4866                     cm_user_t * userp,
4867                      cm_req_t * reqp)
4868 {
4869     long code = 0;
4870     cm_file_lock_t *fileLock;
4871     osi_queue_t *q, *qn;
4872     int n_unlocks = 0;
4873
4874     osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
4875              scp, key.process_id, key.session_id, key.file_id);
4876     osi_Log1(afsd_logp, "    flags=0x%x", flags);
4877
4878     lock_ObtainWrite(&cm_scacheLock);
4879
4880     for (q = scp->fileLocksH; q; q = qn) {
4881         qn = osi_QNext(q);
4882
4883         fileLock = (cm_file_lock_t *)
4884             ((char *) q - offsetof(cm_file_lock_t, fileq));
4885
4886 #ifdef DEBUG
4887         osi_Log4(afsd_logp, "   Checking lock[0x%x] range[%d,+%d] type[%d]",
4888                  fileLock,
4889                  (unsigned long) fileLock->range.offset,
4890                  (unsigned long) fileLock->range.length,
4891                 fileLock->lockType);
4892         osi_Log4(afsd_logp, "     key<0x%x, 0x%x, 0x%x> flags[0x%x]",
4893                  fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
4894                  fileLock->flags);
4895
4896         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4897             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4898             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4899                      fileLock->fid.cell,
4900                      fileLock->fid.volume,
4901                      fileLock->fid.vnode,
4902                      fileLock->fid.unique);
4903             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4904                      fileLock->scp->fid.cell,
4905                      fileLock->scp->fid.volume,
4906                      fileLock->scp->fid.vnode,
4907                      fileLock->scp->fid.unique);
4908             osi_assertx(FALSE, "invalid fid value");
4909         }
4910 #endif
4911
4912         if (!IS_LOCK_DELETED(fileLock) &&
4913             cm_KeyEquals(&fileLock->key, &key, flags)) {
4914             osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4915                     fileLock->range.offset,
4916                     fileLock->range.length,
4917                     fileLock->lockType);
4918
4919             if (scp->fileLocksT == q)
4920                 scp->fileLocksT = osi_QPrev(q);
4921             osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4922
4923             if (IS_LOCK_CLIENTONLY(fileLock)) {
4924                 scp->clientLocks--;
4925             } else if (IS_LOCK_ACCEPTED(fileLock)) {
4926                 if (fileLock->lockType == LockRead)
4927                     scp->sharedLocks--;
4928                 else
4929                     scp->exclusiveLocks--;
4930             }
4931
4932             fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4933
4934             cm_ReleaseUser(fileLock->userp);
4935             cm_ReleaseSCacheNoLock(scp);
4936
4937             fileLock->userp = NULL;
4938             fileLock->scp = NULL;
4939
4940             n_unlocks++;
4941         }
4942     }
4943
4944     lock_ReleaseWrite(&cm_scacheLock);
4945
4946     if (n_unlocks == 0) {
4947         osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4948         osi_Log3(afsd_logp, "   Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4949                  scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4950         
4951         return 0;
4952     }
4953
4954     osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4955
4956     osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4957     osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4958     osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4959
4960     if (!SERVERLOCKS_ENABLED(scp)) {
4961         osi_Log0(afsd_logp, "  Skipping server lock for scp");
4962         goto done;
4963     }
4964
4965     /* Ideally we would go through the rest of the locks to determine
4966      * if one or more locks that were formerly in WAITUNLOCK can now
4967      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4968      * scp->sharedLocks accordingly.  However, the retrying of locks
4969      * in that manner is done cm_RetryLock() manually.
4970      */
4971
4972     if (scp->serverLock == LockWrite &&
4973         scp->exclusiveLocks == 0 &&
4974         scp->sharedLocks > 0) {
4975         /* The serverLock should be downgraded to LockRead */
4976         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
4977
4978         /* Make sure there are no dirty buffers left. */
4979         code = cm_FSync(scp, userp, reqp, TRUE);
4980
4981         /* since scp->serverLock looked sane, we are going to assume
4982            that we have a valid server lock. */
4983         scp->lockDataVersion = scp->dataVersion;
4984         osi_Log1(afsd_logp, "  dataVersion on scp = %I64d", scp->dataVersion);
4985
4986         code = cm_IntReleaseLock(scp, userp, reqp);
4987
4988         if (code) {
4989             /* so we couldn't release it.  Just let the lock be for now */
4990             code = 0;
4991             goto done;
4992         } else {
4993             scp->serverLock = -1;
4994         }
4995
4996         code = cm_IntSetLock(scp, userp, LockRead, reqp);
4997
4998         if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4999             scp->serverLock = LockRead;
5000         } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5001             /* We lost a race condition.  Although we have a valid
5002                lock on the file, the data has changed and essentially
5003                we have lost the lock we had during the transition. */
5004
5005             osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
5006             osi_Log2(afsd_logp, "  Data versions before=%I64d, after=%I64d",
5007                      scp->lockDataVersion,
5008                      scp->dataVersion);
5009             
5010             code = cm_IntReleaseLock(scp, userp, reqp);
5011
5012             code = CM_ERROR_INVAL;
5013             scp->serverLock = -1;
5014         }
5015
5016         if (code != 0 &&
5017             (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5018                 (scp->serverLock == -1)) {
5019                 /* Oopsie */
5020                 cm_LockMarkSCacheLost(scp);
5021             }
5022
5023         /* failure here has no bearing on the return value of
5024            cm_Unlock() */
5025         code = 0;
5026
5027     } else if (scp->serverLock != (-1) &&
5028               scp->exclusiveLocks == 0 &&
5029               scp->sharedLocks == 0) {
5030         /* The serverLock should be released entirely */
5031
5032         if (scp->serverLock == LockWrite) {
5033             /* Make sure there are no dirty buffers left. */
5034             code = cm_FSync(scp, userp, reqp, TRUE);
5035         }
5036
5037         code = cm_IntReleaseLock(scp, userp, reqp);
5038
5039         if (code == 0)
5040             scp->serverLock = (-1);
5041     }
5042
5043  done:
5044
5045     osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
5046     osi_Log4(afsd_logp, "   Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5047              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5048              (int)(signed char) scp->serverLock);
5049
5050     return code;
5051 }
5052
5053 long cm_Unlock(cm_scache_t *scp, 
5054                unsigned char sLockType,
5055                LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
5056                cm_key_t key, 
5057                afs_uint32 flags,
5058                cm_user_t *userp, 
5059                cm_req_t *reqp)
5060 {
5061     long code = 0;
5062     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
5063     cm_file_lock_t *fileLock;
5064     osi_queue_t *q;
5065     int release_userp = FALSE;
5066     int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
5067     int lock_found  = 0;
5068     LARGE_INTEGER RangeEnd;
5069
5070     osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
5071              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
5072     osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
5073              key.process_id, key.session_id, key.file_id, flags);
5074
5075     if (!exact_match)
5076         RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
5077
5078   try_again:
5079     lock_ObtainRead(&cm_scacheLock);
5080
5081     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5082         fileLock = (cm_file_lock_t *)
5083             ((char *) q - offsetof(cm_file_lock_t, fileq));
5084
5085 #ifdef DEBUG
5086         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5087             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5088             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5089                      fileLock->fid.cell,
5090                      fileLock->fid.volume,
5091                      fileLock->fid.vnode,
5092                      fileLock->fid.unique);
5093             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5094                      fileLock->scp->fid.cell,
5095                      fileLock->scp->fid.volume,
5096                      fileLock->scp->fid.vnode,
5097                      fileLock->scp->fid.unique);
5098             osi_assertx(FALSE, "invalid fid value");
5099         }
5100 #endif
5101         if (exact_match) {
5102             if (!IS_LOCK_DELETED(fileLock) &&
5103                  cm_KeyEquals(&fileLock->key, &key, 0) &&
5104                  fileLock->range.offset == LOffset.QuadPart &&
5105                  fileLock->range.length == LLength.QuadPart) {
5106                 lock_found = 1;
5107                 break;
5108             }
5109         } else {
5110
5111             if (!IS_LOCK_DELETED(fileLock) &&
5112                  cm_KeyEquals(&fileLock->key, &key, 0) &&
5113                  fileLock->range.offset >= LOffset.QuadPart &&
5114                  fileLock->range.offset < RangeEnd.QuadPart &&
5115                  (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5116                 lock_found = 1;
5117                 break;
5118             }
5119         }
5120     }
5121
5122     if (!q) {
5123         lock_ReleaseRead(&cm_scacheLock);
5124
5125         if (lock_found && !exact_match) {
5126             code = 0;
5127             goto done;
5128         } else {
5129             osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5130         
5131             /* The lock didn't exist anyway. *shrug* */
5132             return CM_ERROR_RANGE_NOT_LOCKED;
5133         }
5134     }
5135
5136     /* discard lock record */
5137     lock_ConvertRToW(&cm_scacheLock);
5138     if (scp->fileLocksT == q)
5139         scp->fileLocksT = osi_QPrev(q);
5140     osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5141
5142     /*
5143      * Don't delete it here; let the daemon delete it, to simplify
5144      * the daemon's traversal of the list.
5145      */
5146
5147     if (IS_LOCK_CLIENTONLY(fileLock)) {
5148         scp->clientLocks--;
5149     } else if (IS_LOCK_ACCEPTED(fileLock)) {
5150         if (fileLock->lockType == LockRead)
5151             scp->sharedLocks--;
5152         else
5153             scp->exclusiveLocks--;
5154     }
5155
5156     fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5157     if (userp != NULL) {
5158         cm_ReleaseUser(fileLock->userp);
5159     } else {
5160         userp = fileLock->userp;
5161         release_userp = TRUE;
5162     }
5163     fileLock->userp = NULL;
5164     cm_ReleaseSCacheNoLock(scp);
5165     fileLock->scp = NULL;
5166     lock_ReleaseWrite(&cm_scacheLock);
5167
5168     if (!SERVERLOCKS_ENABLED(scp)) {
5169         osi_Log0(afsd_logp, "   Skipping server locks for scp");
5170         goto done;
5171     }
5172
5173     /* Ideally we would go through the rest of the locks to determine
5174      * if one or more locks that were formerly in WAITUNLOCK can now
5175      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
5176      * scp->sharedLocks accordingly.  However, the retrying of locks
5177      * in that manner is done cm_RetryLock() manually.
5178      */
5179
5180     if (scp->serverLock == LockWrite &&
5181         scp->exclusiveLocks == 0 &&
5182         scp->sharedLocks > 0) {
5183
5184         /* The serverLock should be downgraded to LockRead */
5185         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
5186
5187         /* Make sure there are no dirty buffers left. */
5188         code = cm_FSync(scp, userp, reqp, TRUE);
5189
5190         /* Since we already had a lock, we assume that there is a
5191            valid server lock. */
5192         scp->lockDataVersion = scp->dataVersion;
5193         osi_Log1(afsd_logp, "   dataVersion on scp is %I64d", scp->dataVersion);
5194
5195         /* before we downgrade, make sure that we have enough
5196            permissions to get the read lock. */
5197         code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5198         if (code != 0) {
5199
5200             osi_Log0(afsd_logp, "  SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5201
5202             code = 0;
5203             goto done;
5204         }
5205
5206         code = cm_IntReleaseLock(scp, userp, reqp);
5207
5208         if (code) {
5209             /* so we couldn't release it.  Just let the lock be for now */
5210             code = 0;
5211             goto done;
5212         } else {
5213             scp->serverLock = -1;
5214         }
5215
5216         code = cm_IntSetLock(scp, userp, LockRead, reqp);
5217
5218         if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5219             scp->serverLock = LockRead;
5220         } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5221             /* Lost a race.  We obtained a new lock, but that is
5222                meaningless since someone modified the file
5223                inbetween. */
5224
5225             osi_Log0(afsd_logp,
5226                      "Data version mismatch while downgrading lock");
5227             osi_Log2(afsd_logp,
5228                      "  Data versions before=%I64d, after=%I64d",
5229                      scp->lockDataVersion,
5230                      scp->dataVersion);
5231             
5232             code = cm_IntReleaseLock(scp, userp, reqp);
5233
5234             scp->serverLock = -1;
5235             code = CM_ERROR_INVAL;
5236         }
5237
5238         if (code != 0 &&
5239             (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5240                 (scp->serverLock == -1)) {
5241                 /* Oopsie */
5242                 cm_LockMarkSCacheLost(scp);
5243             }
5244
5245         /* failure here has no bearing on the return value of
5246            cm_Unlock() */
5247         code = 0;
5248
5249     } else if (scp->serverLock != (-1) &&
5250               scp->exclusiveLocks == 0 &&
5251               scp->sharedLocks == 0) {
5252         /* The serverLock should be released entirely */
5253
5254         if (scp->serverLock == LockWrite) {
5255             /* Make sure there are no dirty buffers left. */
5256             code = cm_FSync(scp, userp, reqp, TRUE);
5257         }
5258
5259         code = cm_IntReleaseLock(scp, userp, reqp);
5260
5261         if (code == 0) {
5262             scp->serverLock = (-1);
5263         }
5264     }
5265
5266     if (release_userp) {
5267         cm_ReleaseUser(userp);
5268         release_userp = FALSE;
5269     }
5270
5271     if (!exact_match)
5272         goto try_again;         /* might be more than one lock in the range */
5273
5274  done:
5275
5276     osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5277     osi_Log4(afsd_logp, "  leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5278              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5279              (int)(signed char) scp->serverLock);
5280
5281     return code;
5282 }
5283
5284 /* called with scp->rw held */
5285 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5286 {
5287     cm_file_lock_t *fileLock;
5288     osi_queue_t *q;
5289
5290     osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5291
5292     /* cm_scacheLock needed because we are modifying fileLock->flags */
5293     lock_ObtainWrite(&cm_scacheLock);
5294
5295     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5296         fileLock = 
5297             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5298
5299         if (IS_LOCK_ACTIVE(fileLock) &&
5300             !IS_LOCK_CLIENTONLY(fileLock)) {
5301             if (fileLock->lockType == LockRead)
5302                 scp->sharedLocks--;
5303             else
5304                 scp->exclusiveLocks--;
5305
5306             fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5307         }
5308     }
5309
5310     scp->serverLock = -1;
5311     scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5312     lock_ReleaseWrite(&cm_scacheLock);
5313 }
5314
5315 /* Called with no relevant locks held */
5316 void cm_CheckLocks()
5317 {
5318     osi_queue_t *q, *nq;
5319     cm_file_lock_t *fileLock;
5320     cm_req_t req;
5321     AFSFid tfid;
5322     AFSVolSync volSync;
5323     cm_conn_t *connp;
5324     long code;
5325     struct rx_connection * rxconnp;
5326     cm_scache_t * scp;
5327
5328     memset(&volSync, 0, sizeof(volSync));
5329
5330     cm_InitReq(&req);
5331
5332     lock_ObtainWrite(&cm_scacheLock);
5333
5334     cm_lockRefreshCycle++;
5335
5336     osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5337
5338     for (q = cm_allFileLocks; q; q = nq) {
5339         fileLock = (cm_file_lock_t *) q;
5340         nq = osi_QNext(q);
5341         code = -1;
5342
5343         if (IS_LOCK_DELETED(fileLock)) {
5344
5345             osi_QRemove(&cm_allFileLocks, q);
5346             cm_PutFileLock(fileLock);
5347
5348         } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5349
5350             /* Server locks must have been enabled for us to have
5351                received an active non-client-only lock. */
5352             osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5353
5354             scp = fileLock->scp;
5355             osi_assertx(scp != NULL, "null cm_scache_t");
5356
5357             cm_HoldSCacheNoLock(scp);
5358
5359 #ifdef DEBUG
5360             if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5361                 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5362                 osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5363                          fileLock->fid.cell,
5364                          fileLock->fid.volume,
5365                          fileLock->fid.vnode,
5366                          fileLock->fid.unique);
5367                 osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5368                          fileLock->scp->fid.cell,
5369                          fileLock->scp->fid.volume,
5370                          fileLock->scp->fid.vnode,
5371                          fileLock->scp->fid.unique);
5372                 osi_assertx(FALSE, "invalid fid");
5373             }
5374 #endif
5375             /* Server locks are extended once per scp per refresh
5376                cycle. */
5377             if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5378
5379                 int scp_done = FALSE;
5380
5381                 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5382
5383                 lock_ReleaseWrite(&cm_scacheLock);
5384                 lock_ObtainWrite(&scp->rw);
5385
5386                 /* did the lock change while we weren't holding the lock? */
5387                 if (!IS_LOCK_ACTIVE(fileLock))
5388                     goto post_syncopdone;
5389
5390                 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5391                                  CM_SCACHESYNC_NEEDCALLBACK
5392                                  | CM_SCACHESYNC_GETSTATUS
5393                                  | CM_SCACHESYNC_LOCK);
5394
5395                 if (code) {
5396                     osi_Log1(afsd_logp,
5397                              "cm_CheckLocks SyncOp failure code 0x%x", code);
5398                     goto post_syncopdone;
5399                 }
5400
5401                 /* cm_SyncOp releases scp->rw during which the lock
5402                    may get released. */
5403                 if (!IS_LOCK_ACTIVE(fileLock))
5404                     goto pre_syncopdone;
5405
5406                 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5407                     cm_fid_t cfid;
5408                     cm_user_t * userp;
5409
5410                     tfid.Volume = scp->fid.volume;
5411                     tfid.Vnode = scp->fid.vnode;
5412                     tfid.Unique = scp->fid.unique;
5413                     cfid = scp->fid;
5414                     userp = fileLock->userp;
5415                     
5416                     osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d", 
5417                              fileLock,
5418                              scp,
5419                              (int) scp->serverLock);
5420
5421                     lock_ReleaseWrite(&scp->rw);
5422
5423                     do {
5424                         code = cm_ConnFromFID(&cfid, userp,
5425                                        &req, &connp);
5426                         if (code) 
5427                             break;
5428
5429                         rxconnp = cm_GetRxConn(connp);
5430                         code = RXAFS_ExtendLock(rxconnp, &tfid,
5431                                                 &volSync);
5432                         rx_PutConnection(rxconnp);
5433
5434                         osi_Log1(afsd_logp, "   ExtendLock returns %d", code);
5435
5436                     } while (cm_Analyze(connp, userp, &req,
5437                                         &cfid, &volSync, NULL, NULL,
5438                                         code));
5439
5440                     code = cm_MapRPCError(code, &req);
5441
5442                     lock_ObtainWrite(&scp->rw);
5443
5444                     if (code) {
5445                         osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5446                     } else {
5447                         osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5448                         scp->lockDataVersion = scp->dataVersion;
5449                     }
5450
5451                     if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5452                         scp->lockDataVersion == scp->dataVersion) {
5453                         int lockType;
5454
5455                         lockType =
5456                             (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5457
5458                         /* we might still have a chance to obtain a
5459                            new lock */
5460
5461                         code = cm_IntSetLock(scp, userp, lockType, &req);
5462
5463                         if (code) {
5464                             code = CM_ERROR_INVAL;
5465                         } else if (scp->lockDataVersion != scp->dataVersion) {
5466
5467                             /* now check if we still have the file at
5468                                the right data version. */
5469                             osi_Log1(afsd_logp,
5470                                      "Data version mismatch on scp 0x%p",
5471                                      scp);
5472                             osi_Log2(afsd_logp,
5473                                      "   Data versions: before=%I64d, after=%I64d",
5474                                      scp->lockDataVersion,
5475                                      scp->dataVersion);
5476
5477                             code = cm_IntReleaseLock(scp, userp, &req);
5478
5479                             code = CM_ERROR_INVAL;
5480                         }
5481                     }
5482
5483                     if (code == EINVAL || code == CM_ERROR_INVAL ||
5484                         code == CM_ERROR_BADFD) {
5485                         cm_LockMarkSCacheLost(scp);
5486                     }
5487
5488                 } else {
5489                     /* interestingly, we have found an active lock
5490                        belonging to an scache that has no
5491                        serverLock */
5492                     cm_LockMarkSCacheLost(scp);
5493                 }
5494
5495                 scp_done = TRUE;
5496
5497             pre_syncopdone:
5498
5499                 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5500
5501             post_syncopdone:
5502                 lock_ReleaseWrite(&scp->rw);
5503
5504                 lock_ObtainWrite(&cm_scacheLock);
5505
5506                 if (code == 0) {
5507                     fileLock->lastUpdate = time(NULL);
5508                 }
5509                 
5510                 if (scp_done)
5511                     scp->lastRefreshCycle = cm_lockRefreshCycle;
5512
5513             } else {
5514                 /* we have already refreshed the locks on this scp */
5515                 fileLock->lastUpdate = time(NULL);
5516             }
5517
5518             cm_ReleaseSCacheNoLock(scp);
5519
5520         } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5521             /* TODO: Check callbacks */
5522         }
5523     }
5524
5525     lock_ReleaseWrite(&cm_scacheLock);
5526     osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5527 }
5528
5529 /* NOT called with scp->rw held. */
5530 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5531 {
5532     long code = 0;
5533     cm_scache_t *scp = NULL;
5534     cm_file_lock_t *fileLock;
5535     osi_queue_t *q;
5536     cm_req_t req;
5537     int newLock = -1;
5538     int force_client_lock = FALSE;
5539     int has_insert = FALSE;
5540     int check_data_version = FALSE;
5541
5542     cm_InitReq(&req);
5543
5544     if (client_is_dead) {
5545         code = CM_ERROR_TIMEDOUT;
5546         goto updateLock;
5547     }
5548
5549     lock_ObtainRead(&cm_scacheLock);
5550
5551     osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5552     osi_Log4(afsd_logp, "    offset(%x:%x) length(%x:%x)",
5553              (unsigned)(oldFileLock->range.offset >> 32),
5554              (unsigned)(oldFileLock->range.offset & 0xffffffff),
5555              (unsigned)(oldFileLock->range.length >> 32),
5556              (unsigned)(oldFileLock->range.length & 0xffffffff));
5557     osi_Log4(afsd_logp, "    key<0x%x,0x%x,0x%x> flags=%x",
5558              oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5559              (unsigned)(oldFileLock->flags));
5560
5561     /* if the lock has already been granted, then we have nothing to do */
5562     if (IS_LOCK_ACTIVE(oldFileLock)) {
5563         lock_ReleaseRead(&cm_scacheLock);
5564         osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5565         return 0;
5566     }
5567
5568     /* we can't do anything with lost or deleted locks at the moment. */
5569     if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5570         code = CM_ERROR_BADFD;
5571         osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5572         lock_ReleaseRead(&cm_scacheLock);
5573         goto updateLock;
5574     }
5575
5576     scp = oldFileLock->scp;
5577
5578     osi_assertx(scp != NULL, "null cm_scache_t");
5579
5580     lock_ReleaseRead(&cm_scacheLock);
5581     lock_ObtainWrite(&scp->rw);
5582
5583     code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5584                              oldFileLock->userp,
5585                              &req, &has_insert);
5586
5587     if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5588         if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5589         force_client_lock = TRUE;
5590         }
5591         code = 0;
5592     } else if (code) {
5593         lock_ReleaseWrite(&scp->rw);
5594         return code;
5595     }
5596
5597     lock_ObtainWrite(&cm_scacheLock);
5598
5599     /* Check if we already have a sufficient server lock to allow this
5600        lock to go through. */
5601     if (IS_LOCK_WAITLOCK(oldFileLock) &&
5602         (!SERVERLOCKS_ENABLED(scp) ||
5603          scp->serverLock == oldFileLock->lockType ||
5604          scp->serverLock == LockWrite)) {
5605
5606         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5607
5608         if (SERVERLOCKS_ENABLED(scp)) {
5609             osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock.  Granting",
5610                      (int) scp->serverLock);
5611         } else {
5612             osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5613         }
5614
5615         lock_ReleaseWrite(&cm_scacheLock);
5616         lock_ReleaseWrite(&scp->rw);
5617
5618         return 0;
5619     }
5620
5621     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5622
5623         /* check if the conflicting locks have dissappeared already */
5624         for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5625
5626             fileLock = (cm_file_lock_t *)
5627                 ((char *) q - offsetof(cm_file_lock_t, fileq));
5628
5629             if (IS_LOCK_LOST(fileLock)) {
5630                 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5631                     code = CM_ERROR_BADFD;
5632                     oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5633                     osi_Log1(afsd_logp, "    found lost lock %p for same key.  Marking lock as lost",
5634                              fileLock);
5635                     break;
5636                 } else if (fileLock->lockType == LockWrite &&
5637                            INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5638                     osi_Log1(afsd_logp, "    found conflicting LOST lock %p", fileLock);
5639                     code = CM_ERROR_WOULDBLOCK;
5640                     break;
5641                 }
5642             }
5643
5644             if (IS_LOCK_ACCEPTED(fileLock) &&
5645                 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5646
5647                 if (oldFileLock->lockType != LockRead ||
5648                    fileLock->lockType != LockRead) {
5649
5650                     osi_Log1(afsd_logp, "    found conflicting lock %p", fileLock);
5651                     code = CM_ERROR_WOULDBLOCK;
5652                     break;
5653                 }
5654             }
5655         }
5656     }
5657
5658     if (code != 0) {
5659         lock_ReleaseWrite(&cm_scacheLock);
5660         lock_ReleaseWrite(&scp->rw);
5661
5662         goto handleCode;
5663     }
5664
5665     /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5666        If it is WAITUNLOCK, then we didn't find any conflicting lock
5667        but we haven't verfied whether the serverLock is sufficient to
5668        assert it.  If it is WAITLOCK, then the serverLock is
5669        insufficient to assert it. Eitherway, we are ready to accept
5670        the lock as either ACTIVE or WAITLOCK depending on the
5671        serverLock. */
5672
5673     /* First, promote the WAITUNLOCK to a WAITLOCK */
5674     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5675         if (oldFileLock->lockType == LockRead)
5676             scp->sharedLocks++;
5677         else
5678             scp->exclusiveLocks++;
5679
5680         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5681         oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5682     }
5683
5684     osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5685
5686     if (force_client_lock ||
5687         !SERVERLOCKS_ENABLED(scp) ||
5688         scp->serverLock == oldFileLock->lockType ||
5689         (oldFileLock->lockType == LockRead &&
5690          scp->serverLock == LockWrite)) {
5691
5692         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5693
5694         if ((force_client_lock ||
5695              !SERVERLOCKS_ENABLED(scp)) &&
5696             !IS_LOCK_CLIENTONLY(oldFileLock)) {
5697
5698             oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5699
5700             if (oldFileLock->lockType == LockRead)
5701                 scp->sharedLocks--;
5702             else
5703                 scp->exclusiveLocks--;
5704
5705             scp->clientLocks++;
5706         }
5707
5708         lock_ReleaseWrite(&cm_scacheLock);
5709         lock_ReleaseWrite(&scp->rw);
5710
5711         return 0;
5712
5713     } else {
5714         cm_user_t * userp;
5715
5716         code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5717                          CM_SCACHESYNC_NEEDCALLBACK
5718                          | CM_SCACHESYNC_GETSTATUS
5719                          | CM_SCACHESYNC_LOCK);
5720         if (code) {
5721             osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5722             lock_ReleaseWrite(&cm_scacheLock);
5723             goto post_syncopdone;
5724         }
5725
5726         if (!IS_LOCK_WAITLOCK(oldFileLock))
5727             goto pre_syncopdone;
5728
5729         userp = oldFileLock->userp;
5730
5731 #ifndef AGGRESSIVE_LOCKS
5732         newLock = oldFileLock->lockType;
5733 #else
5734         newLock = LockWrite;
5735 #endif
5736
5737         if (has_insert) {
5738             /* if has_insert is non-zero, then:
5739                - the lock a LockRead
5740                - we don't have permission to get a LockRead
5741                - we do have permission to get a LockWrite
5742                - the server supports VICED_CAPABILITY_WRITELOCKACL
5743             */
5744
5745             newLock = LockWrite;
5746         }
5747
5748         lock_ReleaseWrite(&cm_scacheLock);
5749
5750         /* when we get here, either we have a read-lock and want a
5751            write-lock or we don't have any locks and we want some
5752            lock. */
5753
5754         if (scp->serverLock == LockRead) {
5755
5756             osi_assertx(newLock == LockWrite, "!LockWrite");
5757
5758             osi_Log0(afsd_logp, "  Attempting to UPGRADE from LockRead to LockWrite");
5759
5760             scp->lockDataVersion = scp->dataVersion;
5761             check_data_version = TRUE;
5762
5763             code = cm_IntReleaseLock(scp, userp, &req);
5764
5765             if (code)
5766                 goto pre_syncopdone;
5767             else
5768                 scp->serverLock = -1;
5769         }
5770
5771         code = cm_IntSetLock(scp, userp, newLock, &req);
5772
5773         if (code == 0) {
5774             if (scp->dataVersion != scp->lockDataVersion) {
5775                 /* we lost a race.  too bad */
5776
5777                 osi_Log0(afsd_logp,
5778                          "  Data version mismatch while upgrading lock.");
5779                 osi_Log2(afsd_logp,
5780                          "  Data versions before=%I64d, after=%I64d",
5781                          scp->lockDataVersion,
5782                          scp->dataVersion);
5783                 osi_Log1(afsd_logp,
5784                          "  Releasing stale lock for scp 0x%x", scp);
5785
5786                 code = cm_IntReleaseLock(scp, userp, &req);
5787
5788                 scp->serverLock = -1;
5789
5790                 code = CM_ERROR_INVAL;
5791
5792                 cm_LockMarkSCacheLost(scp);
5793             } else {
5794                 scp->serverLock = newLock;
5795             }
5796         }
5797
5798     pre_syncopdone:
5799         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5800     post_syncopdone:
5801         ;
5802     }
5803
5804   handleCode:
5805     if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5806         lock_ObtainWrite(&cm_scacheLock);
5807         if (scp->fileLocksT == &oldFileLock->fileq)
5808             scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5809         osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5810         lock_ReleaseWrite(&cm_scacheLock);
5811     }
5812     lock_ReleaseWrite(&scp->rw);
5813
5814   updateLock:
5815     lock_ObtainWrite(&cm_scacheLock);
5816     if (code == 0) {
5817         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5818     } else if (code != CM_ERROR_WOULDBLOCK) {
5819         oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5820         cm_ReleaseUser(oldFileLock->userp);
5821         oldFileLock->userp = NULL;
5822         if (oldFileLock->scp) {
5823             cm_ReleaseSCacheNoLock(oldFileLock->scp);
5824             oldFileLock->scp = NULL;
5825         }
5826     }
5827     lock_ReleaseWrite(&cm_scacheLock);
5828
5829     return code;
5830 }
5831
5832 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
5833 {
5834     cm_key_t key;
5835
5836     key.process_id = process_id;
5837     key.session_id = session_id;
5838     key.file_id = file_id;
5839
5840     return key;
5841 }
5842
5843 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5844 {
5845     return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5846         ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
5847 }
5848
5849 void cm_ReleaseAllLocks(void)
5850 {
5851     cm_scache_t *scp;
5852     cm_req_t req;
5853     cm_user_t *userp;
5854     cm_key_t   key;
5855     cm_file_lock_t *fileLock;
5856     unsigned int i;
5857
5858     for (i = 0; i < cm_data.scacheHashTableSize; i++)
5859     {
5860         for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5861             while (scp->fileLocksH != NULL) {
5862                 lock_ObtainWrite(&scp->rw);
5863                 lock_ObtainWrite(&cm_scacheLock);
5864                 if (!scp->fileLocksH) {
5865                     lock_ReleaseWrite(&cm_scacheLock);
5866                     lock_ReleaseWrite(&scp->rw);
5867                     break;
5868                 }
5869                 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5870                 userp = fileLock->userp;
5871                 cm_HoldUser(userp);
5872                 key = fileLock->key;
5873                 cm_HoldSCacheNoLock(scp);
5874                 lock_ReleaseWrite(&cm_scacheLock);
5875                 cm_UnlockByKey(scp, key, 0, userp, &req);
5876                 cm_ReleaseSCache(scp);
5877                 cm_ReleaseUser(userp);
5878                 lock_ReleaseWrite(&scp->rw);
5879             }
5880         }
5881     }
5882 }