71163be8b5b065597a257b0dee40cada4bd69f98
[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, 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, 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,
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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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