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