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