39988eb5ebeab1b0940bdb7ba17839b7044341d2
[openafs.git] / src / WINNT / afsd / cm_vnodeops.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afs/param.h>
11 #include <afs/stds.h>
12
13 #include <windows.h>
14 #include <winsock2.h>
15 #include <stddef.h>
16 #include <malloc.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <errno.h>
20
21 #include <osi.h>
22
23 #include "afsd.h"
24 #include "smb.h"
25 #include "cm_btree.h"
26
27 #include <strsafe.h>
28
29 #ifdef DEBUG
30 extern void afsi_log(char *pattern, ...);
31 #endif
32
33 int cm_enableServerLocks = 1;
34
35 int cm_followBackupPath = 0;
36
37 /*
38  * Case-folding array.  This was constructed by inspecting of SMBtrace output.
39  * I do not know anything more about it.
40  */
41 unsigned char cm_foldUpper[256] = {
42      0x0,  0x1,  0x2,  0x3,  0x4,  0x5,  0x6,  0x7,
43      0x8,  0x9,  0xa,  0xb,  0xc,  0xd,  0xe,  0xf,
44     0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
45     0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
46     0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
47     0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
48     0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
49     0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
50     0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
51     0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
52     0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
53     0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
54     0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
55     0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
56     0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
57     0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
58     0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
59     0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
60     0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
61     0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
62     0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
63     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
64     0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
65     0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
66     0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
67     0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
68     0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
69     0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
70     0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
71     0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
72     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
73     0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
74 };
75
76 /*
77  * Case-insensitive string comparison.  We used to use stricmp, but it doesn't
78  * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
79  * upper-case u-umlaut).
80  */
81 int cm_stricmp(const char *str1, const char *str2)
82 {
83     char c1, c2;
84
85     while (1) {
86         if (*str1 == 0)
87             if (*str2 == 0)
88                 return 0;
89             else
90                 return -1;
91         if (*str2 == 0)
92             return 1;
93         c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
94         c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
95         if (c1 < c2)
96             return -1;
97         if (c1 > c2)
98             return 1;
99     }
100 }
101
102
103
104 /* return success if we can open this file in this mode */
105 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
106                   cm_req_t *reqp)
107 {
108     long rights;
109     long code;
110
111     rights = 0;
112     if (openMode != 1) 
113         rights |= PRSFS_READ;
114     if (openMode == 1 || openMode == 2 || trunc) 
115         rights |= PRSFS_WRITE;
116         
117     lock_ObtainWrite(&scp->rw);
118
119     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
120                       CM_SCACHESYNC_GETSTATUS
121                      | CM_SCACHESYNC_NEEDCALLBACK
122                      | CM_SCACHESYNC_LOCK);
123
124     if (code == 0 && 
125         ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
126         scp->fileType == CM_SCACHETYPE_FILE) {
127
128         cm_key_t key;
129         unsigned int sLockType;
130         LARGE_INTEGER LOffset, LLength;
131
132         /* Check if there's some sort of lock on the file at the
133            moment. */
134
135         key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
136
137         if (rights & PRSFS_WRITE)
138             sLockType = 0;
139         else
140             sLockType = LOCKING_ANDX_SHARED_LOCK;
141
142         LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
143         LOffset.LowPart  = CM_FLSHARE_OFFSET_LOW;
144         LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
145         LLength.LowPart  = CM_FLSHARE_LENGTH_LOW;
146
147         code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
148
149         if (code == 0) {
150             cm_Unlock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp);
151         } else {
152             /* In this case, we allow the file open to go through even
153                though we can't enforce mandatory locking on the
154                file. */
155             if (code == CM_ERROR_NOACCESS &&
156                 !(rights & PRSFS_WRITE))
157                 code = 0;
158             else {
159                 switch (code) {
160                 case CM_ERROR_ALLOFFLINE:
161                 case CM_ERROR_ALLDOWN:
162                 case CM_ERROR_ALLBUSY:
163                 case CM_ERROR_TIMEDOUT:
164                 case CM_ERROR_RETRY:
165                 case CM_ERROR_WOULDBLOCK:
166                     break;
167                 default:
168                     code = CM_ERROR_SHARING_VIOLATION;
169                 }
170             }
171         }
172
173     } else if (code != 0) {
174         goto _done;
175     }
176
177     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
178
179  _done:
180
181     lock_ReleaseWrite(&scp->rw);
182
183     return code;
184 }
185
186 /* return success if we can open this file in this mode */
187 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
188                     unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp, 
189                     cm_lock_data_t **ldpp)
190 {
191     long rights;
192     long code;
193
194     osi_assertx(ldpp != NULL, "null cm_lock_data_t");
195     *ldpp = NULL;
196
197     /* Always allow delete; the RPC will tell us if it's OK */
198     if (desiredAccess == DELETE)
199         return 0;
200
201     rights = 0;
202
203     if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
204         rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
205
206     /* We used to require PRSFS_WRITE if createDisp was 4
207        (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
208        However, we don't need to do that since the existence of the
209        scp implies that we don't need to create it. */
210     if (desiredAccess & AFS_ACCESS_WRITE)
211         rights |= PRSFS_WRITE;
212
213     lock_ObtainWrite(&scp->rw);
214
215     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
216                       CM_SCACHESYNC_GETSTATUS
217                      | CM_SCACHESYNC_NEEDCALLBACK
218                      | CM_SCACHESYNC_LOCK);
219
220     /*
221      * If the open will fail because the volume is readonly, then we will
222      * return an access denied error instead.  This is to help brain-dead
223      * apps run correctly on replicated volumes.
224      * See defect 10007 for more information.
225      */
226     if (code == CM_ERROR_READONLY)
227         code = CM_ERROR_NOACCESS;
228
229     if (code == 0 &&
230              ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
231              scp->fileType == CM_SCACHETYPE_FILE) {
232         cm_key_t key;
233         unsigned int sLockType;
234         LARGE_INTEGER LOffset, LLength;
235
236         /* Check if there's some sort of lock on the file at the
237            moment. */
238
239         key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
240         if (rights & PRSFS_WRITE)
241             sLockType = 0;
242         else
243             sLockType = LOCKING_ANDX_SHARED_LOCK;
244
245         /* single byte lock at offset 0x0100 0000 0000 0000 */
246         LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
247         LOffset.LowPart  = CM_FLSHARE_OFFSET_LOW;
248         LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
249         LLength.LowPart  = CM_FLSHARE_LENGTH_LOW;
250
251         code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
252
253         if (code == 0) {
254             (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
255             if (!*ldpp) {
256                 code = ENOMEM;
257                 goto _done;
258             }
259
260             (*ldpp)->key = key;
261             (*ldpp)->sLockType = sLockType;
262             (*ldpp)->LOffset.HighPart = LOffset.HighPart;
263             (*ldpp)->LOffset.LowPart = LOffset.LowPart;
264             (*ldpp)->LLength.HighPart = LLength.HighPart;
265             (*ldpp)->LLength.LowPart = LLength.LowPart;
266         } else {
267             /* In this case, we allow the file open to go through even
268                though we can't enforce mandatory locking on the
269                file. */
270             if (code == CM_ERROR_NOACCESS &&
271                 !(rights & PRSFS_WRITE))
272                 code = 0;
273             else {
274                 switch (code) {
275                 case CM_ERROR_ALLOFFLINE:
276                 case CM_ERROR_ALLDOWN:
277                 case CM_ERROR_ALLBUSY:
278                 case CM_ERROR_TIMEDOUT:
279                 case CM_ERROR_RETRY:
280                 case CM_ERROR_WOULDBLOCK:
281                     break;
282                 default:
283                     code = CM_ERROR_SHARING_VIOLATION;
284                 }
285             }
286         }
287     } else if (code != 0) {
288         goto _done;
289     }
290
291  _done:
292     lock_ReleaseWrite(&scp->rw);
293
294     osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
295     return code;
296 }
297
298 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, 
299                                cm_lock_data_t ** ldpp)
300 {
301     osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
302     lock_ObtainWrite(&scp->rw);
303     if (*ldpp) {
304         cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength, 
305                   (*ldpp)->key, 0, userp, reqp);
306         free(*ldpp);
307         *ldpp = NULL;
308     }
309     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
310     lock_ReleaseWrite(&scp->rw);
311     return 0;
312 }
313 /*
314  * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
315  * done in three steps:
316  * (1) open for deletion (NT_CREATE_AND_X)
317  * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
318  * (3) close (CLOSE)
319  * We must not do the RPC until step 3.  But if we are going to return an error
320  * code (e.g. directory not empty), we must return it by step 2, otherwise most
321  * clients will not notice it.  So we do a preliminary check.  For deleting
322  * files, this is almost free, since we have already done the RPC to get the
323  * parent directory's status bits.  But for deleting directories, we must do an
324  * additional RPC to get the directory's data to check if it is empty.  Sigh.
325  */
326 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
327         cm_req_t *reqp)
328 {
329     long code;
330     osi_hyper_t thyper;
331     cm_buf_t *bufferp;
332     cm_dirEntry_t *dep = 0;
333     unsigned short *hashTable;
334     unsigned int i, idx;
335     int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
336     int releaseLock = 0;
337
338     /* First check permissions */
339     lock_ObtainWrite(&scp->rw);
340     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
341                       CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
342     if (!code)
343         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
344     lock_ReleaseWrite(&scp->rw);
345     if (code)
346         return code;
347
348     /* If deleting directory, must be empty */
349
350     if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
351         return code;
352
353     thyper.HighPart = 0; thyper.LowPart = 0;
354     code = buf_Get(scp, &thyper, reqp, &bufferp);
355     if (code)
356         return code;
357
358     lock_ObtainMutex(&bufferp->mx);
359     lock_ObtainWrite(&scp->rw);
360     releaseLock = 1;
361     while (1) {
362         code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
363                           CM_SCACHESYNC_NEEDCALLBACK
364                           | CM_SCACHESYNC_READ
365                           | CM_SCACHESYNC_BUFLOCKED);
366         if (code)
367             goto done;
368
369         if (cm_HaveBuffer(scp, bufferp, 1))
370             break;
371
372         /* otherwise, load the buffer and try again */
373         lock_ReleaseMutex(&bufferp->mx);
374         code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
375         lock_ReleaseWrite(&scp->rw);
376         lock_ObtainMutex(&bufferp->mx);
377         lock_ObtainWrite(&scp->rw);
378         cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
379         if (code)
380             goto done;
381     }
382
383     lock_ReleaseWrite(&scp->rw);
384     releaseLock = 0;
385
386     /* We try to determine emptiness without looking beyond the first page,
387      * and without assuming "." and ".." are present and are on the first
388      * page (though these assumptions might, after all, be reasonable).
389      */
390     hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
391     for (i=0; i<128; i++) {
392         idx = ntohs(hashTable[i]);
393         while (idx) {
394             if (idx >= 64) {
395                 BeyondPage = 1;
396                 break;
397             }
398             dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
399             if (strcmp(dep->name, ".") == 0)
400                 HaveDot = 1;
401             else if (strcmp(dep->name, "..") == 0)
402                 HaveDotDot = 1;
403             else {
404                 code = CM_ERROR_NOTEMPTY;
405                 goto done;
406             }
407             idx = ntohs(dep->next);
408         }
409     }
410     if (BeyondPage && HaveDot && HaveDotDot)
411         code = CM_ERROR_NOTEMPTY;
412     else
413         code = 0;
414   done:   
415     lock_ReleaseMutex(&bufferp->mx);
416     buf_Release(bufferp);
417     if (releaseLock)
418         lock_ReleaseWrite(&scp->rw);
419     return code;
420 }       
421
422 /*
423  * Iterate through all entries in a directory.
424  * When the function funcp is called, the buffer is locked but the
425  * directory vnode is not.
426  *
427  * If the retscp parameter is not NULL, the parmp must be a 
428  * cm_lookupSearch_t object.  
429  */
430 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
431                  osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
432                  cm_scache_t **retscp)
433 {
434     char *tp;
435     long code;
436     cm_dirEntry_t *dep = 0;
437     cm_buf_t *bufferp;
438     long temp;
439     osi_hyper_t dirLength;
440     osi_hyper_t bufferOffset;
441     osi_hyper_t curOffset;
442     osi_hyper_t thyper;
443     long entryInDir;
444     long entryInBuffer;
445     cm_pageHeader_t *pageHeaderp;
446     int slotInPage;
447     long nextEntryCookie;
448     int numDirChunks;   /* # of 32 byte dir chunks in this entry */
449         
450     /* get the directory size */
451     lock_ObtainWrite(&scp->rw);
452     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
453                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
454     lock_ReleaseWrite(&scp->rw);
455     if (code)
456         return code;
457         
458     if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
459         return CM_ERROR_NOTDIR;
460
461     if (retscp)                         /* if this is a lookup call */
462     {
463         cm_lookupSearch_t*      sp = parmp;
464
465         if (
466 #ifdef AFS_FREELANCE_CLIENT
467         /* Freelance entries never end up in the DNLC because they
468          * do not have an associated cm_server_t
469          */
470             !(cm_freelanceEnabled &&
471             sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
472               sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
473 #else /* !AFS_FREELANCE_CLIENT */
474             TRUE
475 #endif
476             ) 
477         {
478             int casefold = sp->caseFold;
479             sp->caseFold = 0; /* we have a strong preference for exact matches */
480             if ( *retscp = cm_dnlcLookup(scp, sp))      /* dnlc hit */
481             {
482                 sp->caseFold = casefold;
483                 return 0;
484             }
485             sp->caseFold = casefold;
486         }
487
488         /*
489          * see if we can find it using the directory hash tables.
490          * we can only do exact matches, since the hash is case
491          * sensitive.
492          */
493         if (funcp != (cm_DirFuncp_t)cm_BPlusDirFoo)
494         {
495             cm_dirOp_t dirop;
496 #ifdef USE_BPLUS
497             int usedBplus = 0;
498 #endif
499
500             code = ENOENT;
501
502             code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
503             if (code == 0) {
504
505 #ifdef USE_BPLUS
506                 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
507                 if (code != EINVAL)
508                     usedBplus = 1;
509                 else
510 #endif
511                     code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
512
513                 cm_EndDirOp(&dirop);
514             }
515
516             if (code == 0) {
517                 /* found it */
518                 sp->found = TRUE;
519                 sp->ExactFound = TRUE;
520                 *retscp = NULL; /* force caller to call cm_GetSCache() */
521                 return 0;
522             }
523 #ifdef USE_BPLUS
524             if (usedBplus) {
525                 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
526                     /* found it */
527                     sp->found = TRUE;
528                     sp->ExactFound = FALSE;
529                     *retscp = NULL; /* force caller to call cm_GetSCache() */
530                     return 0;
531                 }
532
533                 return CM_ERROR_BPLUS_NOMATCH;
534             }
535 #endif
536         }
537     }   
538
539     /*
540      * XXX We only get the length once.  It might change when we drop the
541      * lock.
542      */
543     dirLength = scp->length;
544
545     bufferp = NULL;
546     bufferOffset.LowPart = bufferOffset.HighPart = 0;
547     if (startOffsetp)
548         curOffset = *startOffsetp;
549     else {
550         curOffset.HighPart = 0;
551         curOffset.LowPart = 0;
552     }   
553
554     while (1) {
555         /* make sure that curOffset.LowPart doesn't point to the first
556          * 32 bytes in the 2nd through last dir page, and that it
557          * doesn't point at the first 13 32-byte chunks in the first
558          * dir page, since those are dir and page headers, and don't
559          * contain useful information.
560          */
561         temp = curOffset.LowPart & (2048-1);
562         if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
563             /* we're in the first page */
564             if (temp < 13*32) temp = 13*32;
565         }
566         else {
567             /* we're in a later dir page */
568             if (temp < 32) temp = 32;
569         }       
570                 
571         /* make sure the low order 5 bits are zero */
572         temp &= ~(32-1);
573                 
574         /* now put temp bits back ito curOffset.LowPart */
575         curOffset.LowPart &= ~(2048-1);
576         curOffset.LowPart |= temp;
577
578         /* check if we've passed the dir's EOF */
579         if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
580             break;
581                 
582         /* see if we can use the bufferp we have now; compute in which
583          * page the current offset would be, and check whether that's
584          * the offset of the buffer we have.  If not, get the buffer.
585          */
586         thyper.HighPart = curOffset.HighPart;
587         thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
588         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
589             /* wrong buffer */
590             if (bufferp) {
591                 lock_ReleaseMutex(&bufferp->mx);
592                 buf_Release(bufferp);
593                 bufferp = NULL;
594             }
595
596             code = buf_Get(scp, &thyper, reqp, &bufferp);
597             if (code) {
598                 /* if buf_Get() fails we do not have a buffer object to lock */
599                 bufferp = NULL;
600                 break;
601             }
602
603             lock_ObtainMutex(&bufferp->mx);
604             bufferOffset = thyper;
605
606             /* now get the data in the cache */
607             while (1) {
608                 lock_ObtainWrite(&scp->rw);
609                 code = cm_SyncOp(scp, bufferp, userp, reqp,
610                                   PRSFS_LOOKUP,
611                                   CM_SCACHESYNC_NEEDCALLBACK
612                                   | CM_SCACHESYNC_READ
613                                   | CM_SCACHESYNC_BUFLOCKED);
614                 if (code) {
615                     lock_ReleaseWrite(&scp->rw);
616                     break;
617                 }
618                 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
619                                 
620                 if (cm_HaveBuffer(scp, bufferp, 1)) {
621                     lock_ReleaseWrite(&scp->rw);
622                     break;
623                 }
624
625                 /* otherwise, load the buffer and try again */
626                 lock_ReleaseMutex(&bufferp->mx);
627                 code = cm_GetBuffer(scp, bufferp, NULL, userp,
628                                     reqp);
629                 lock_ReleaseWrite(&scp->rw);
630                 lock_ObtainMutex(&bufferp->mx);
631                 if (code) 
632                     break;
633             }
634             if (code) {
635                 lock_ReleaseMutex(&bufferp->mx);
636                 buf_Release(bufferp);
637                 bufferp = NULL;
638                 break;
639             }
640         }       /* if (wrong buffer) ... */
641            
642         /* now we have the buffer containing the entry we're interested
643          * in; copy it out if it represents a non-deleted entry.
644          */
645         entryInDir = curOffset.LowPart & (2048-1);
646         entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
647
648         /* page header will help tell us which entries are free.  Page
649          * header can change more often than once per buffer, since
650          * AFS 3 dir page size may be less than (but not more than) a
651          * buffer package buffer.
652          */
653         /* only look intra-buffer */
654         temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
655         temp &= ~(2048 - 1);    /* turn off intra-page bits */
656         pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
657
658         /* now determine which entry we're looking at in the page.  If
659          * it is free (there's a free bitmap at the start of the dir),
660          * we should skip these 32 bytes.
661          */
662         slotInPage = (entryInDir & 0x7e0) >> 5;
663         if (!(pageHeaderp->freeBitmap[slotInPage>>3]
664                & (1 << (slotInPage & 0x7)))) {
665             /* this entry is free */
666             numDirChunks = 1;   /* only skip this guy */
667             goto nextEntry;
668         }
669
670         tp = bufferp->datap + entryInBuffer;
671         dep = (cm_dirEntry_t *) tp;     /* now points to AFS3 dir entry */
672
673         /* while we're here, compute the next entry's location, too,
674          * since we'll need it when writing out the cookie into the
675          * dir listing stream.
676          */
677         numDirChunks = cm_NameEntries(dep->name, NULL);
678                 
679         /* compute the offset of the cookie representing the next entry */
680         nextEntryCookie = curOffset.LowPart
681             + (CM_DIR_CHUNKSIZE * numDirChunks);
682
683         if (dep->fid.vnode != 0) {
684             /* this is one of the entries to use: it is not deleted */
685             code = (*funcp)(scp, dep, parmp, &curOffset);
686             if (code) 
687                 break;
688         }       /* if we're including this name */
689                 
690       nextEntry:
691         /* and adjust curOffset to be where the new cookie is */
692         thyper.HighPart = 0;
693         thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
694         curOffset = LargeIntegerAdd(thyper, curOffset);
695     }           /* while copying data for dir listing */
696
697     /* release the mutex */
698     if (bufferp) {
699         lock_ReleaseMutex(&bufferp->mx);
700         buf_Release(bufferp);
701     }
702     return code;
703 }
704
705 int cm_NoneUpper(normchar_t *s)
706 {
707     normchar_t c;
708     while (c = *s++)
709         if (c >= 'A' && c <= 'Z')
710             return 0;
711     return 1;
712 }
713
714 int cm_NoneLower(normchar_t *s)
715 {
716     normchar_t c;
717     while (c = *s++)
718         if (c >= 'a' && c <= 'z')
719             return 0;
720     return 1;
721 }
722
723 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
724                          osi_hyper_t *offp)
725 {
726     cm_lookupSearch_t *sp;
727     int match;
728     normchar_t matchName[MAX_PATH];
729     int looking_for_short_name = FALSE;
730
731     sp = (cm_lookupSearch_t *) rockp;
732
733     if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
734         /* Can't normalize FS string. */
735         return 0;
736     }
737
738     if (sp->caseFold)
739         match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
740     else
741         match = cm_NormStrCmp(matchName, sp->nsearchNamep);
742
743     if (match != 0
744         && sp->hasTilde
745         && !cm_Is8Dot3(matchName)) {
746
747         cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
748         if (sp->caseFold)
749             match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
750         else
751             match = cm_NormStrCmp(matchName, sp->nsearchNamep);
752         looking_for_short_name = TRUE;
753     }
754
755     if (match != 0)
756         return 0;
757
758     sp->found = 1;
759     if (!sp->caseFold) 
760         sp->ExactFound = 1;
761
762     if (!sp->caseFold || looking_for_short_name) {
763         cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
764         return CM_ERROR_STOPNOW;
765     }
766
767     /*
768      * If we get here, we are doing a case-insensitive search, and we
769      * have found a match.  Now we determine what kind of match it is:
770      * exact, lower-case, upper-case, or none of the above.  This is done
771      * in order to choose among matches, if there are more than one.
772      */
773
774     /* Exact matches are the best. */
775     match = cm_NormStrCmp(matchName, sp->nsearchNamep);
776     if (match == 0) {
777         sp->ExactFound = 1;
778         cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
779         return CM_ERROR_STOPNOW;
780     }
781
782     /* Lower-case matches are next. */
783     if (sp->LCfound)
784         return 0;
785     if (cm_NoneUpper(matchName)) {
786         sp->LCfound = 1;
787         goto inexact;
788     }
789
790     /* Upper-case matches are next. */
791     if (sp->UCfound)
792         return 0;
793     if (cm_NoneLower(matchName)) {
794         sp->UCfound = 1;
795         goto inexact;
796     }
797
798     /* General matches are last. */
799     if (sp->NCfound)
800         return 0;
801     sp->NCfound = 1;
802
803   inexact:
804     cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
805     return 0;
806 }       
807
808 /* read the contents of a mount point into the appropriate string.
809  * called with write locked scp, and returns with locked scp.
810  */
811 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
812 {
813     long code;
814     cm_buf_t *bufp = NULL;
815     osi_hyper_t thyper;
816     int tlen;
817
818     if (scp->mountPointStringp[0]) 
819         return 0;
820         
821 #ifdef AFS_FREELANCE_CLIENT
822     /* File servers do not have data for freelance entries */
823     if (cm_freelanceEnabled &&
824         scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
825         scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
826     {       
827         code = cm_FreelanceFetchMountPointString(scp);
828     } else 
829 #endif /* AFS_FREELANCE_CLIENT */        
830     {
831         /* otherwise, we have to read it in */
832         lock_ReleaseWrite(&scp->rw);
833
834         thyper.LowPart = thyper.HighPart = 0;
835         code = buf_Get(scp, &thyper, reqp, &bufp);
836
837         lock_ObtainWrite(&scp->rw);
838         if (code)
839             return code;
840
841         while (1) {
842             code = cm_SyncOp(scp, bufp, userp, reqp, 0,
843                               CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
844             if (code)
845                 goto done;
846
847             cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
848
849             if (cm_HaveBuffer(scp, bufp, 0)) 
850                 break;
851
852             /* otherwise load buffer */
853             code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
854             if (code)
855                 goto done;
856         }
857         /* locked, has callback, has valid data in buffer */
858         if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1) 
859             return CM_ERROR_TOOBIG;
860         if (tlen <= 0) {
861             code = CM_ERROR_INVAL;
862             goto done;
863         }
864
865         /* someone else did the work while we were out */
866         if (scp->mountPointStringp[0]) {
867             code = 0;
868             goto done;
869         }
870
871         /* otherwise, copy out the link */
872         memcpy(scp->mountPointStringp, bufp->datap, tlen);
873
874         /* now make it null-terminated.  Note that the original contents of a
875          * link that is a mount point is "#volname." where "." is there just to
876          * be turned into a null.  That is, we can trash the last char of the
877          * link without damaging the vol name.  This is a stupid convention,
878          * but that's the protocol.
879          */
880         scp->mountPointStringp[tlen-1] = 0;
881         code = 0;
882
883       done:
884         if (bufp) 
885             buf_Release(bufp);
886     }
887     return code;
888 }
889
890
891 /* called with a locked scp and chases the mount point, yielding outScpp.
892  * scp remains write locked, just for simplicity of describing the interface.
893  */
894 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
895                          cm_req_t *reqp, cm_scache_t **outScpp)
896 {
897     fschar_t *cellNamep = NULL;
898     fschar_t *volNamep = NULL;
899     afs_uint32 code;
900     fschar_t *cp;
901     fschar_t *mpNamep;
902     cm_volume_t *volp = NULL;
903     cm_cell_t *cellp;
904     fschar_t mtType;
905     cm_fid_t tfid;
906     size_t vnLength;
907     int targetType;
908
909     *outScpp = NULL;
910
911     if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
912         tfid = scp->mountRootFid;
913         lock_ReleaseWrite(&scp->rw);
914         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
915         lock_ObtainWrite(&scp->rw);
916         return code;
917     }
918
919     /* parse the volume name */
920     mpNamep = scp->mountPointStringp;
921     if (!mpNamep[0])
922         return CM_ERROR_NOSUCHPATH;
923     mtType = *scp->mountPointStringp;
924
925     cp = cm_FsStrChr(mpNamep, _FS(':'));
926     if (cp) {
927         /* cellular mount point */
928         cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
929         cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
930         volNamep = cm_FsStrDup(cp+1);
931
932         /* now look up the cell */
933         lock_ReleaseWrite(&scp->rw);
934         cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
935         lock_ObtainWrite(&scp->rw);
936     } else {
937         /* normal mt pt */
938         volNamep = cm_FsStrDup(mpNamep + 1);
939
940 #ifdef AFS_FREELANCE_CLIENT
941         /* 
942          * Mount points in the Freelance cell should default
943          * to the workstation cell.
944          */
945         if (cm_freelanceEnabled &&
946              scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
947              scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
948         {       
949             fschar_t rootCellName[256]="";
950             cm_GetRootCellName(rootCellName);
951             cellp = cm_GetCell(rootCellName, 0);
952         } else 
953 #endif /* AFS_FREELANCE_CLIENT */        
954             cellp = cm_FindCellByID(scp->fid.cell, 0);
955     }
956
957     if (!cellp) {
958         code = CM_ERROR_NOSUCHCELL;
959         goto done;
960     }
961
962     vnLength = cm_FsStrLen(volNamep);
963     if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
964         targetType = BACKVOL;
965     else if (vnLength >= 10
966              && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
967         targetType = ROVOL;
968     else
969         targetType = RWVOL;
970
971     /* check for backups within backups */
972     if (targetType == BACKVOL
973          && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
974          == CM_SCACHEFLAG_RO) {
975         code = CM_ERROR_NOSUCHVOLUME;
976         goto done;
977     }
978
979     /* now we need to get the volume */
980     lock_ReleaseWrite(&scp->rw);
981     if (cm_VolNameIsID(volNamep)) {
982         code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp, 
983                                 CM_GETVOL_FLAG_CREATE, &volp);
984     } else {
985         code = cm_FindVolumeByName(cellp, volNamep, userp, reqp, 
986                                   CM_GETVOL_FLAG_CREATE, &volp);
987     }
988     lock_ObtainWrite(&scp->rw);
989         
990     if (code == 0) {
991         afs_uint32 cell, volume;
992         cm_vol_state_t *statep;
993
994         cell = cellp->cellID;
995         
996         /* if the mt pt originates in a .backup volume (not a .readonly)
997          * and FollowBackupPath is active, and if there is a .backup
998          * volume for the target, then use the .backup of the target
999          * instead of the read-write.
1000          */
1001         if (cm_followBackupPath && 
1002             volp->vol[BACKVOL].ID != 0 &&
1003             (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
1004             (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
1005             ) {
1006             targetType = BACKVOL;
1007         } 
1008         /* if the mt pt is in a read-only volume (not just a
1009          * backup), and if there is a read-only volume for the
1010          * target, and if this is a targetType '#' mount point, use
1011          * the read-only, otherwise use the one specified.
1012          */
1013         else if (mtType == '#' && targetType == RWVOL && 
1014                  (scp->flags & CM_SCACHEFLAG_PURERO) && 
1015                  volp->vol[ROVOL].ID != 0) {
1016             targetType = ROVOL;
1017         }
1018
1019         lock_ObtainWrite(&volp->rw);
1020         statep = cm_VolumeStateByType(volp, targetType);
1021         volume = statep->ID;
1022         statep->dotdotFid = dscp->fid;
1023         lock_ReleaseWrite(&volp->rw);
1024
1025         /* the rest of the fid is a magic number */
1026         cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1027         scp->mountRootGen = cm_data.mountRootGen;
1028
1029         tfid = scp->mountRootFid;
1030         lock_ReleaseWrite(&scp->rw);
1031         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1032         lock_ObtainWrite(&scp->rw);
1033     }
1034
1035   done:
1036     if (volp)
1037         cm_PutVolume(volp);
1038     if (cellNamep)
1039         free(cellNamep);
1040     if (volNamep)
1041         free(volNamep);
1042     return code;
1043 }       
1044
1045 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1046                        cm_req_t *reqp, cm_scache_t **outScpp)
1047 {
1048     long code;
1049     int dnlcHit = 1;    /* did we hit in the dnlc? yes, we did */
1050     cm_scache_t *tscp = NULL;
1051     cm_scache_t *mountedScp;
1052     cm_lookupSearch_t rock;
1053     int getroot;
1054     normchar_t *nnamep = NULL;
1055     fschar_t *fnamep = NULL;
1056
1057     *outScpp = NULL;
1058
1059     memset(&rock, 0, sizeof(rock));
1060
1061     if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1062         && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1063         if (dscp->dotdotFid.volume == 0)
1064             return CM_ERROR_NOSUCHVOLUME;
1065         rock.fid = dscp->dotdotFid;
1066         goto haveFid;
1067     } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1068         rock.fid = dscp->fid;
1069         goto haveFid;
1070     }
1071
1072     nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1073     if (!nnamep) {
1074         code = CM_ERROR_NOSUCHFILE;
1075         goto done;
1076     }
1077     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1078     if (!fnamep) {
1079         code = CM_ERROR_NOSUCHFILE;
1080         goto done;
1081     }
1082
1083     if (flags & CM_FLAG_NOMOUNTCHASE) {
1084         /* In this case, we should go and call cm_Dir* functions
1085            directly since the following cm_ApplyDir() function will
1086            not. */
1087
1088         cm_dirOp_t dirop;
1089 #ifdef USE_BPLUS
1090         int usedBplus = 0;
1091 #endif
1092
1093         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1094         if (code == 0) {
1095 #ifdef USE_BPLUS
1096             code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1097             if (code != EINVAL)
1098                 usedBplus = 1;
1099             else
1100 #endif
1101                 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1102
1103             cm_EndDirOp(&dirop);
1104         }
1105
1106         if (code == 0) {
1107             /* found it */
1108             rock.found = TRUE;
1109             goto haveFid;
1110         }
1111 #ifdef USE_BPLUS
1112         if (usedBplus) {
1113             if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1114                 /* found it */
1115                 code = 0;
1116                 rock.found = TRUE;
1117                 goto haveFid;
1118             }
1119             
1120             return CM_ERROR_BPLUS_NOMATCH;
1121         }
1122 #endif
1123     }
1124
1125     rock.fid.cell = dscp->fid.cell;
1126     rock.fid.volume = dscp->fid.volume;
1127     rock.searchNamep = fnamep;
1128     rock.nsearchNamep = nnamep;
1129     rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1130     rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1131
1132     /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1133     code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1134                        (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1135
1136     /* code == 0 means we fell off the end of the dir, while stopnow means
1137      * that we stopped early, probably because we found the entry we're
1138      * looking for.  Any other non-zero code is an error.
1139      */
1140     if (code && code != CM_ERROR_STOPNOW) {
1141         /* if the cm_scache_t we are searching in is not a directory 
1142          * we must return path not found because the error 
1143          * is to describe the final component not an intermediary
1144          */
1145         if (code == CM_ERROR_NOTDIR) {
1146             if (flags & CM_FLAG_CHECKPATH)
1147                 code = CM_ERROR_NOSUCHPATH;
1148             else
1149                 code = CM_ERROR_NOSUCHFILE;
1150         }
1151         goto done;
1152     }
1153
1154     getroot = (dscp==cm_data.rootSCachep) ;
1155     if (!rock.found) {
1156         if (!cm_freelanceEnabled || !getroot) {
1157             if (flags & CM_FLAG_CHECKPATH)
1158                 code = CM_ERROR_NOSUCHPATH;
1159             else
1160                 code = CM_ERROR_NOSUCHFILE;
1161             goto done;
1162         }
1163         else if (!cm_ClientStrChr(cnamep, '#') &&
1164                  !cm_ClientStrChr(cnamep, '%') &&
1165                  cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1166                  cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1167                  cm_ClientStrCmpI(cnamep, _C("ipc$"))) 
1168         {
1169             /* nonexistent dir on freelance root, so add it */
1170             fschar_t fullname[CELL_MAXNAMELEN + 1] = ".";  /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1171             int  found = 0;
1172
1173             osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S", 
1174                      osi_LogSaveClientString(afsd_logp,cnamep));
1175
1176             /* 
1177              * There is an ugly behavior where a share name "foo" will be searched
1178              * for as "fo".  If the searched for name differs by an already existing
1179              * symlink or mount point in the Freelance directory, do not add the 
1180              * new value automatically.
1181              */
1182
1183             code = -1;
1184             if (cnamep[0] == '.') {
1185                 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1186                     found = 1;
1187                     if (!cm_FreelanceMountPointExists(fullname, 0))
1188                         code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.",
1189                                                     1, &rock.fid);
1190                     if ( cm_FsStrCmpI(&fnamep[1], &fullname[1]) && 
1191                          !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1192                          !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1193                         code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1194                 }
1195             } else {
1196                 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1197                     found = 1;
1198                     if (!cm_FreelanceMountPointExists(fullname, 0))
1199                         code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
1200                     if ( cm_FsStrCmpI(fnamep, fullname) && 
1201                          !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1202                          !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1203                         code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1204                 }
1205             }
1206             if (!found || code) {   /* add mount point failed, so give up */
1207                 if (flags & CM_FLAG_CHECKPATH)
1208                     code = CM_ERROR_NOSUCHPATH;
1209                 else
1210                     code = CM_ERROR_NOSUCHFILE;
1211                 goto done;
1212             }
1213             tscp = NULL;   /* to force call of cm_GetSCache */
1214         } else {
1215             if (flags & CM_FLAG_CHECKPATH)
1216                 code = CM_ERROR_NOSUCHPATH;
1217             else
1218                 code = CM_ERROR_NOSUCHFILE;
1219             goto done;
1220         }
1221     }
1222
1223   haveFid:       
1224     if ( !tscp )    /* we did not find it in the dnlc */
1225     {
1226         dnlcHit = 0; 
1227         code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1228         if (code) 
1229             goto done;
1230     }
1231     /* tscp is now held */
1232
1233     lock_ObtainWrite(&tscp->rw);
1234     code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1235                       CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1236     if (code) { 
1237         lock_ReleaseWrite(&tscp->rw);
1238         cm_ReleaseSCache(tscp);
1239         goto done;
1240     }
1241     cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1242     /* tscp is now locked */
1243
1244     if (!(flags & CM_FLAG_NOMOUNTCHASE)
1245          && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1246         /* mount points are funny: they have a volume name to mount
1247          * the root of.
1248          */
1249         code = cm_ReadMountPoint(tscp, userp, reqp);
1250         if (code == 0)
1251             code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1252                                        &mountedScp);
1253         lock_ReleaseWrite(&tscp->rw);
1254         cm_ReleaseSCache(tscp);
1255         if (code)
1256             goto done;
1257
1258         tscp = mountedScp;
1259     }
1260     else {
1261         lock_ReleaseWrite(&tscp->rw);
1262     }
1263
1264     /* copy back pointer */
1265     *outScpp = tscp;
1266
1267     /* insert scache in dnlc */
1268     if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1269         /* lock the directory entry to prevent racing callback revokes */
1270         lock_ObtainRead(&dscp->rw);
1271         if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1272             /* TODO: reuse nnamep from above */
1273             if (nnamep) 
1274                 free(nnamep);
1275             nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1276             if (nnamep)
1277                 cm_dnlcEnter(dscp, nnamep, tscp);
1278         }
1279         lock_ReleaseRead(&dscp->rw);
1280     }
1281
1282     /* and return */
1283   done:
1284     if (fnamep) {
1285         free (fnamep);
1286         fnamep = NULL;
1287     }
1288     if (nnamep) {
1289         free (nnamep);
1290         nnamep = NULL;
1291     }
1292
1293     return code;
1294 }
1295
1296 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1297 {
1298     clientchar_t *tp;
1299     int prefixCount;
1300
1301     tp = cm_ClientStrRChr(inp, '@');
1302     if (tp == NULL) 
1303         return 0;               /* no @sys */
1304
1305     if (cm_ClientStrCmp(tp, _C("@sys")) != 0) 
1306         return 0;       /* no @sys */
1307
1308     /* caller just wants to know if this is a valid @sys type of name */
1309     if (outp == NULL) 
1310         return 1;
1311
1312     if (index >= cm_sysNameCount)
1313         return -1;
1314
1315     /* otherwise generate the properly expanded @sys name */
1316     prefixCount = (int)(tp - inp);
1317
1318     cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount);       /* copy out "a." from "a.@sys" */
1319     outp[prefixCount] = 0;              /* null terminate the "a." */
1320     cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1321     return 1;
1322 }   
1323
1324 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1325                                 cm_req_t *reqp, cm_scache_t ** outScpp)
1326 {
1327     afs_uint32    code = 0;
1328     fschar_t      cellName[CELL_MAXNAMELEN];
1329     fschar_t      volumeName[VL_MAXNAMELEN];
1330     size_t        len;
1331     fschar_t *        cp;
1332     fschar_t *        tp;
1333     fschar_t *        fnamep = NULL;
1334
1335     cm_cell_t *   cellp = NULL;
1336     cm_volume_t * volp = NULL;
1337     cm_fid_t      fid;
1338     afs_uint32    volume;
1339     int           volType;
1340     int           mountType = RWVOL;
1341
1342     osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1343              osi_LogSaveClientString(afsd_logp, namep));
1344
1345     if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1346         goto _exit_invalid_path;
1347     }
1348
1349     /* namep is assumed to look like the following:
1350
1351        @vol:<cellname>%<volume>\0
1352        or
1353        @vol:<cellname>#<volume>\0
1354
1355      */
1356
1357     fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1358     cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1359     tp = cm_FsStrChr(cp, '%');
1360     if (tp == NULL)
1361         tp = cm_FsStrChr(cp, '#');
1362     if (tp == NULL ||
1363         (len = tp - cp) == 0 ||
1364         len > CELL_MAXNAMELEN)
1365         goto _exit_invalid_path;
1366     cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1367
1368     if (*tp == '#')
1369         mountType = ROVOL;
1370
1371     cp = tp+1;                  /* cp now points to volume, supposedly */
1372     cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1373
1374     /* OK, now we have the cell and the volume */
1375     osi_Log2(afsd_logp, "   Found cell [%s] and volume [%s]",
1376              osi_LogSaveFsString(afsd_logp, cellName),
1377              osi_LogSaveFsString(afsd_logp, volumeName));
1378
1379     cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1380     if (cellp == NULL) {
1381         goto _exit_invalid_path;
1382     }
1383
1384     len = cm_FsStrLen(volumeName);
1385     if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1386         volType = BACKVOL;
1387     else if (len >= 10 &&
1388              cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1389         volType = ROVOL;
1390     else
1391         volType = RWVOL;
1392
1393     if (cm_VolNameIsID(volumeName)) {
1394         code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1395                                 CM_GETVOL_FLAG_CREATE, &volp);
1396     } else {
1397         code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1398                                   CM_GETVOL_FLAG_CREATE, &volp);
1399     }
1400
1401     if (code != 0)
1402         goto _exit_cleanup;
1403
1404     if (volType == BACKVOL)
1405         volume = volp->vol[BACKVOL].ID;
1406     else if (volType == ROVOL ||
1407              (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1408         volume = volp->vol[ROVOL].ID;
1409     else
1410         volume = volp->vol[RWVOL].ID;
1411
1412     cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1413
1414     code = cm_GetSCache(&fid, outScpp, userp, reqp);
1415
1416   _exit_cleanup:
1417     if (fnamep)
1418         free(fnamep);
1419
1420     if (volp)
1421         cm_PutVolume(volp);
1422
1423     if (code == 0)
1424         return code;
1425
1426  _exit_invalid_path:
1427     if (flags & CM_FLAG_CHECKPATH)
1428         return CM_ERROR_NOSUCHPATH;
1429     else
1430         return CM_ERROR_NOSUCHFILE;
1431 }
1432
1433 #ifdef DEBUG_REFCOUNT
1434 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1435                cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1436 #else
1437 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1438                cm_req_t *reqp, cm_scache_t **outScpp)
1439 #endif
1440 {
1441     long code;
1442     clientchar_t tname[AFSPATHMAX];
1443     int sysNameIndex = 0;
1444     cm_scache_t *scp = NULL;
1445
1446 #ifdef DEBUG_REFCOUNT
1447     afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1448     osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1449 #endif
1450
1451     if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1452         if (flags & CM_FLAG_CHECKPATH)
1453             return CM_ERROR_NOSUCHPATH;
1454         else
1455             return CM_ERROR_NOSUCHFILE;
1456     }
1457
1458     if (dscp == cm_data.rootSCachep &&
1459         cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1460         return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1461     }
1462
1463     if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1464         for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1465             code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1466             if (code > 0) {
1467                 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1468 #ifdef DEBUG_REFCOUNT
1469                 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);
1470                 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1471 #endif
1472
1473                 if (code == 0) {
1474                     *outScpp = scp;
1475                     return 0;
1476                 }
1477                 if (scp) {
1478                     cm_ReleaseSCache(scp);
1479                     scp = NULL;
1480                 }
1481             } else {
1482                 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1483 #ifdef DEBUG_REFCOUNT
1484                 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);
1485                 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1486 #endif
1487                 *outScpp = scp;
1488                 return code;
1489             }
1490         }
1491     } else {
1492         code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1493 #ifdef DEBUG_REFCOUNT
1494         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);
1495         osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1496 #endif
1497         *outScpp = scp;
1498         return code;
1499     }
1500
1501     /* None of the possible sysName expansions could be found */
1502     if (flags & CM_FLAG_CHECKPATH)
1503         return CM_ERROR_NOSUCHPATH;
1504     else
1505         return CM_ERROR_NOSUCHFILE;
1506 }
1507
1508 /*! \brief Unlink a file name
1509
1510   Encapsulates a call to RXAFS_RemoveFile().
1511
1512   \param[in] dscp cm_scache_t pointing at the directory containing the
1513       name to be unlinked.
1514
1515   \param[in] fnamep Original name to be unlinked.  This is the
1516       name that will be passed into the RXAFS_RemoveFile() call.
1517       This parameter is optional.  If not provided, the value will
1518       be looked up.
1519
1520   \param[in] came Client name to be unlinked.  This name will be used
1521       to update the local directory caches.
1522
1523   \param[in] userp cm_user_t for the request.
1524
1525   \param[in] reqp Request tracker.
1526  
1527  */
1528 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1529                cm_user_t *userp, cm_req_t *reqp)
1530 {
1531     long code;
1532     cm_conn_t *connp;
1533     AFSFid afsFid;
1534     int sflags;
1535     AFSFetchStatus newDirStatus;
1536     AFSVolSync volSync;
1537     struct rx_connection * rxconnp;
1538     cm_dirOp_t dirop;
1539     cm_scache_t *scp = NULL;
1540     int free_fnamep = FALSE;
1541
1542     memset(&volSync, 0, sizeof(volSync));
1543
1544     if (fnamep == NULL) {
1545         code = -1;
1546 #ifdef USE_BPLUS
1547         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1548         if (code == 0) {
1549             code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1550             if (code == 0)
1551                 free_fnamep = TRUE;
1552             cm_EndDirOp(&dirop);
1553         }
1554 #endif
1555         if (code)
1556             goto done;
1557     }
1558
1559 #ifdef AFS_FREELANCE_CLIENT
1560     if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1561         /* deleting a mount point from the root dir. */
1562         code = cm_FreelanceRemoveMount(fnamep);
1563         goto done;
1564     }
1565 #endif  
1566
1567     code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1568
1569     /* make sure we don't screw up the dir status during the merge */
1570     code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
1571
1572     lock_ObtainWrite(&dscp->rw);
1573     sflags = CM_SCACHESYNC_STOREDATA;
1574     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1575     lock_ReleaseWrite(&dscp->rw);
1576     if (code) {
1577         cm_EndDirOp(&dirop);
1578         goto done;
1579     }
1580
1581     /* make the RPC */
1582     afsFid.Volume = dscp->fid.volume;
1583     afsFid.Vnode = dscp->fid.vnode;
1584     afsFid.Unique = dscp->fid.unique;
1585
1586     osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1587     do {
1588         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1589         if (code)
1590             continue;
1591
1592         rxconnp = cm_GetRxConn(connp);
1593         code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1594                                 &newDirStatus, &volSync);
1595         rx_PutConnection(rxconnp);
1596
1597     } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1598     code = cm_MapRPCError(code, reqp);
1599
1600     if (code)
1601         osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1602     else
1603         osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1604
1605     if (dirop.scp) {
1606         lock_ObtainWrite(&dirop.scp->dirlock);
1607         dirop.lockType = CM_DIRLOCK_WRITE;
1608     }
1609     lock_ObtainWrite(&dscp->rw);
1610     cm_dnlcRemove(dscp, cnamep);
1611     cm_SyncOpDone(dscp, NULL, sflags);
1612     if (code == 0) {
1613         cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1614     } else if (code == CM_ERROR_NOSUCHFILE) {
1615         /* windows would not have allowed the request to delete the file 
1616          * if it did not believe the file existed.  therefore, we must 
1617          * have an inconsistent view of the world.
1618          */
1619         dscp->cbServerp = NULL;
1620     }
1621     lock_ReleaseWrite(&dscp->rw);
1622
1623     if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1624         cm_DirDeleteEntry(&dirop, fnamep);
1625 #ifdef USE_BPLUS
1626         cm_BPlusDirDeleteEntry(&dirop, cnamep);
1627 #endif
1628     }
1629     cm_EndDirOp(&dirop);
1630
1631     if (scp) {
1632         cm_ReleaseSCache(scp);
1633         if (code == 0) {
1634             lock_ObtainWrite(&scp->rw);
1635             if (--scp->linkCount == 0)
1636                 scp->flags |= CM_SCACHEFLAG_DELETED;
1637             cm_DiscardSCache(scp);
1638             lock_ReleaseWrite(&scp->rw);
1639         }
1640     }
1641
1642   done:
1643     if (free_fnamep)
1644         free(fnamep);
1645
1646     return code;
1647 }
1648
1649 /* called with a write locked vnode, and fills in the link info.
1650  * returns this the vnode still write locked.
1651  */
1652 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1653 {
1654     long code;
1655     cm_buf_t *bufp;
1656     long temp;
1657     osi_hyper_t thyper;
1658
1659     lock_AssertWrite(&linkScp->rw);
1660     if (!linkScp->mountPointStringp[0]) {
1661         
1662 #ifdef AFS_FREELANCE_CLIENT
1663         /* File servers do not have data for freelance entries */
1664         if (cm_freelanceEnabled &&
1665             linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1666             linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1667         {
1668             code = cm_FreelanceFetchMountPointString(linkScp);
1669         } else 
1670 #endif /* AFS_FREELANCE_CLIENT */        
1671         {
1672             /* read the link data from the file server*/
1673             lock_ReleaseWrite(&linkScp->rw);
1674             thyper.LowPart = thyper.HighPart = 0;
1675             code = buf_Get(linkScp, &thyper, reqp, &bufp);
1676             lock_ObtainWrite(&linkScp->rw);
1677             if (code) 
1678                 return code;
1679             while (1) {
1680                 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1681                                   CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1682                 if (code) {
1683                     buf_Release(bufp);
1684                     return code;
1685                 }
1686                 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1687
1688                 if (cm_HaveBuffer(linkScp, bufp, 0)) 
1689                     break;
1690
1691                 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1692                 if (code) {
1693                     buf_Release(bufp);
1694                     return code;
1695                 }
1696             } /* while loop to get the data */
1697
1698             /* now if we still have no link read in,
1699              * copy the data from the buffer */
1700             if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1701                 buf_Release(bufp);
1702                 return CM_ERROR_TOOBIG;
1703             }       
1704
1705             /* otherwise, it fits; make sure it is still null (could have
1706              * lost race with someone else referencing this link above),
1707              * and if so, copy in the data.
1708              */
1709             if (!linkScp->mountPointStringp[0]) {
1710                 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1711                 linkScp->mountPointStringp[temp] = 0;   /* null terminate */
1712             }
1713             buf_Release(bufp);
1714         }
1715         
1716         if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1717             linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1718
1719     }   /* don't have sym link contents cached */
1720
1721     return 0;
1722 }       
1723
1724 /* called with a held vnode and a path suffix, with the held vnode being a
1725  * symbolic link.  Our goal is to generate a new path to interpret, and return
1726  * this new path in newSpaceBufferp.  If the new vnode is relative to a dir
1727  * other than the directory containing the symbolic link, then the new root is
1728  * returned in *newRootScpp, otherwise a null is returned there.
1729  */
1730 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1731                      cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1732                      cm_user_t *userp, cm_req_t *reqp)
1733 {
1734     long code = 0;
1735     long len;
1736     fschar_t *linkp;
1737     cm_space_t *tsp;
1738
1739     *newRootScpp = NULL;
1740     *newSpaceBufferp = NULL;
1741
1742     lock_ObtainWrite(&linkScp->rw);
1743     code = cm_HandleLink(linkScp, userp, reqp);
1744     if (code)
1745         goto done;
1746
1747     /* if we may overflow the buffer, bail out; buffer is signficantly
1748      * bigger than max path length, so we don't really have to worry about
1749      * being a little conservative here.
1750      */
1751     if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1752         >= CM_UTILS_SPACESIZE) {
1753         code = CM_ERROR_TOOBIG;
1754         goto done;
1755     }
1756
1757     tsp = cm_GetSpace();
1758     linkp = linkScp->mountPointStringp;
1759     if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1760         if (strlen(linkp) > cm_mountRootLen)
1761             StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1762         else
1763             tsp->data[0] = 0;
1764         *newRootScpp = cm_data.rootSCachep;
1765         cm_HoldSCache(cm_data.rootSCachep);
1766     } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1767         if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName)))) 
1768         {
1769             char * p = &linkp[len + 3];
1770             if (strnicmp(p, "all", 3) == 0)
1771                 p += 4;
1772
1773             StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1774             for (p = tsp->data; *p; p++) {
1775                 if (*p == '\\')
1776                     *p = '/';
1777             }
1778             *newRootScpp = cm_data.rootSCachep;
1779             cm_HoldSCache(cm_data.rootSCachep);
1780         } else {
1781             linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1782             StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1783             code = CM_ERROR_PATH_NOT_COVERED;
1784         }
1785     } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1786                 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1787         linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1788         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1789         code = CM_ERROR_PATH_NOT_COVERED;
1790     } else if (*linkp == '\\' || *linkp == '/') {
1791 #if 0   
1792         /* formerly, this was considered to be from the AFS root,
1793          * but this seems to create problems.  instead, we will just
1794          * reject the link */
1795         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1796         *newRootScpp = cm_data.rootSCachep;
1797         cm_HoldSCache(cm_data.rootSCachep);
1798 #else
1799         /* we still copy the link data into the response so that 
1800          * the user can see what the link points to
1801          */
1802         linkScp->fileType = CM_SCACHETYPE_INVALID;
1803         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1804         code = CM_ERROR_NOSUCHPATH;
1805 #endif  
1806     } else {
1807         /* a relative link */
1808         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1809     }
1810     if (pathSuffixp[0] != 0) {  /* if suffix string is non-null */
1811         StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1812         StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1813     }
1814
1815     if (code == 0) {
1816         clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1817         if (cpath != NULL) {
1818         cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1819         free(cpath);
1820         *newSpaceBufferp = tsp;
1821     } else {
1822             code = CM_ERROR_NOSUCHPATH;
1823         }
1824     } 
1825
1826     if (code != 0) {
1827         cm_FreeSpace(tsp);
1828
1829         if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1830             cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1831         }
1832     }
1833
1834  done:
1835     lock_ReleaseWrite(&linkScp->rw);
1836     return code;
1837 }
1838 #ifdef DEBUG_REFCOUNT
1839 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1840                  cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1841                  cm_scache_t **outScpp, 
1842                  char * file, long line)
1843 #else
1844 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1845               cm_user_t *userp, clientchar_t *tidPathp,
1846               cm_req_t *reqp, cm_scache_t **outScpp)
1847 #endif
1848 {
1849     long code;
1850     clientchar_t *tp;                   /* ptr moving through input buffer */
1851     clientchar_t tc;                    /* temp char */
1852     int haveComponent;          /* has new component started? */
1853     clientchar_t component[AFSPATHMAX]; /* this is the new component */
1854     clientchar_t *cp;                   /* component name being assembled */
1855     cm_scache_t *tscp;          /* current location in the hierarchy */
1856     cm_scache_t *nscp;          /* next dude down */
1857     cm_scache_t *dirScp;        /* last dir we searched */
1858     cm_scache_t *linkScp;       /* new root for the symlink we just
1859     * looked up */
1860     cm_space_t *psp;            /* space for current path, if we've hit
1861     * any symlinks */
1862     cm_space_t *tempsp;         /* temp vbl */
1863     clientchar_t *restp;                /* rest of the pathname to interpret */
1864     int symlinkCount;           /* count of # of symlinks traversed */
1865     int extraFlag;              /* avoid chasing mt pts for dir cmd */
1866     int phase = 1;              /* 1 = tidPathp, 2 = pathp */
1867 #define MAX_FID_COUNT 512
1868     cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1869     int fid_count = 0;          /* number of fids processed in this path walk */
1870     int i;
1871
1872     *outScpp = NULL;
1873
1874 #ifdef DEBUG_REFCOUNT
1875     afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1876     osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1877              rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>", 
1878              flags);
1879 #endif
1880
1881     tp = tidPathp;
1882     if (tp == NULL) {
1883         tp = pathp;
1884         phase = 2;
1885     }
1886     if (tp == NULL) {
1887         tp = _C("");
1888     }
1889     haveComponent = 0;
1890     psp = NULL;
1891     tscp = rootSCachep;
1892     cm_HoldSCache(tscp);
1893     symlinkCount = 0;
1894     dirScp = NULL;
1895
1896
1897     while (1) {
1898         tc = *tp++;
1899
1900         /* map Unix slashes into DOS ones so we can interpret Unix
1901          * symlinks properly
1902          */
1903         if (tc == '/') 
1904             tc = '\\';
1905
1906         if (!haveComponent) {
1907             if (tc == '\\') {
1908                 continue;
1909             } else if (tc == 0) {
1910                 if (phase == 1) {
1911                     phase = 2;
1912                     tp = pathp;
1913                     continue;
1914                 }
1915                 code = 0;
1916                 break;
1917             } else {
1918                 haveComponent = 1;
1919                 cp = component;
1920                 *cp++ = tc;
1921             }
1922         } else {
1923             /* we have a component here */
1924             if (tc == 0 || tc == '\\') {
1925                 /* end of the component; we're at the last
1926                  * component if tc == 0.  However, if the last
1927                  * is a symlink, we have more to do.
1928                  */
1929                 *cp++ = 0;      /* add null termination */
1930                 extraFlag = 0;
1931                 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1932                     extraFlag = CM_FLAG_NOMOUNTCHASE;
1933                 code = cm_Lookup(tscp, component,
1934                                  flags | extraFlag,
1935                                  userp, reqp, &nscp);
1936
1937                 if (code == 0) {
1938                     if (!cm_ClientStrCmp(component,_C("..")) ||
1939                         !cm_ClientStrCmp(component,_C("."))) {
1940                         /* 
1941                          * roll back the fid list until we find the
1942                          * fid that matches where we are now.  Its not
1943                          * necessarily one or two fids because they
1944                          * might have been symlinks or mount points or
1945                          * both that were crossed.
1946                          */
1947                         for ( i=fid_count-1; i>=0; i--) {
1948                             if (!cm_FidCmp(&nscp->fid, &fids[i]))
1949                                 break;
1950                         }
1951                         fid_count = i+1;
1952                     } else {
1953                         /* add the new fid to the list */
1954                         if (fid_count == MAX_FID_COUNT) {
1955                             code = CM_ERROR_TOO_MANY_SYMLINKS;
1956                             cm_ReleaseSCache(nscp);
1957                             nscp = NULL;
1958                             break;
1959                         }       
1960                         fids[fid_count++] = nscp->fid;
1961                     }
1962                 }
1963
1964                 if (code) {
1965                     cm_ReleaseSCache(tscp);
1966                     if (dirScp)
1967                         cm_ReleaseSCache(dirScp);
1968                     if (psp) 
1969                         cm_FreeSpace(psp);
1970                     if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) && 
1971                         tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1972                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1973                         return CM_ERROR_NOSUCHPATH;
1974                     } else {
1975                         osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1976                         return code;
1977                     }
1978                 }
1979
1980                 haveComponent = 0;      /* component done */
1981                 if (dirScp)
1982                     cm_ReleaseSCache(dirScp);
1983                 dirScp = tscp;          /* for some symlinks */
1984                 tscp = nscp;            /* already held */
1985                 nscp = NULL;
1986                 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1987                     code = 0;
1988                     if (dirScp) {
1989                         cm_ReleaseSCache(dirScp);
1990                         dirScp = NULL;
1991                     }
1992                     break;
1993                 }
1994
1995                 /* now, if tscp is a symlink, we should follow it and
1996                  * assemble the path again.
1997                  */
1998                 lock_ObtainWrite(&tscp->rw);
1999                 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2000                                   CM_SCACHESYNC_GETSTATUS
2001                                   | CM_SCACHESYNC_NEEDCALLBACK);
2002                 if (code) {
2003                     lock_ReleaseWrite(&tscp->rw);
2004                     cm_ReleaseSCache(tscp);
2005                     tscp = NULL;
2006                     if (dirScp) {
2007                         cm_ReleaseSCache(dirScp);
2008                         dirScp = NULL;
2009                     }
2010                     break;
2011                 }
2012                 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2013
2014                 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2015                     /* this is a symlink; assemble a new buffer */
2016                     lock_ReleaseWrite(&tscp->rw);
2017                     if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2018                         cm_ReleaseSCache(tscp);
2019                         tscp = NULL;
2020                         if (dirScp) {
2021                             cm_ReleaseSCache(dirScp);
2022                             dirScp = NULL;
2023                         }
2024                         if (psp) 
2025                             cm_FreeSpace(psp);
2026                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2027                         return CM_ERROR_TOO_MANY_SYMLINKS;
2028                     }
2029                     if (tc == 0) 
2030                         restp = _C("");
2031                     else 
2032                         restp = tp;
2033
2034                     {
2035                         fschar_t * frestp;
2036
2037                         /* TODO: make this better */
2038                         frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2039                         code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2040                         free(frestp);
2041                     }
2042
2043                     if (code == 0 && linkScp != NULL) {
2044                         if (linkScp == cm_data.rootSCachep) {
2045                             fid_count = 0;
2046                             i = 0;
2047                         } else {
2048                             for ( i=0; i<fid_count; i++) {
2049                                 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2050                                     code = CM_ERROR_TOO_MANY_SYMLINKS;
2051                                     cm_ReleaseSCache(linkScp);
2052                                     nscp = NULL;
2053                                     break;
2054                                 }
2055                             }
2056                         }
2057                         if (i == fid_count && fid_count < MAX_FID_COUNT) {
2058                             fids[fid_count++] = linkScp->fid;
2059                         }
2060                     }
2061
2062                     if (code) {
2063                         /* something went wrong */
2064                         cm_ReleaseSCache(tscp);
2065                         tscp = NULL;
2066                         if (dirScp) {
2067                             cm_ReleaseSCache(dirScp);
2068                             dirScp = NULL;
2069                         }
2070                         break;
2071                     }
2072
2073                     /* otherwise, tempsp has the new path,
2074                      * and linkScp is the new root from
2075                      * which to interpret that path.
2076                      * Continue with the namei processing,
2077                      * also doing the bookkeeping for the
2078                      * space allocation and tracking the
2079                      * vnode reference counts.
2080                      */
2081                     if (psp) 
2082                         cm_FreeSpace(psp);
2083                     psp = tempsp;
2084                     tp = psp->wdata;
2085                     cm_ReleaseSCache(tscp);
2086                     tscp = linkScp;
2087                     linkScp = NULL;
2088                     /* already held
2089                      * by AssembleLink
2090                      * now, if linkScp is null, that's
2091                      * AssembleLink's way of telling us that
2092                      * the sym link is relative to the dir
2093                      * containing the link.  We have a ref
2094                      * to it in dirScp, and we hold it now
2095                      * and reuse it as the new spot in the
2096                      * dir hierarchy.
2097                      */
2098                     if (tscp == NULL) {
2099                         tscp = dirScp;
2100                         dirScp = NULL;
2101                     }
2102                 } else {
2103                     /* not a symlink, we may be done */
2104                     lock_ReleaseWrite(&tscp->rw);
2105                     if (tc == 0) {
2106                         if (phase == 1) {
2107                             phase = 2;
2108                             tp = pathp;
2109                             continue;
2110                         }
2111                         if (dirScp) {
2112                             cm_ReleaseSCache(dirScp);
2113                             dirScp = NULL;
2114                         }
2115                         code = 0;
2116                         break;
2117                     }
2118                 }
2119                 if (dirScp) {
2120                     cm_ReleaseSCache(dirScp);
2121                     dirScp = NULL;
2122                 }
2123             } /* end of a component */
2124             else 
2125                 *cp++ = tc;
2126         } /* we have a component */
2127     } /* big while loop over all components */
2128
2129     /* already held */
2130     if (dirScp)
2131         cm_ReleaseSCache(dirScp);
2132     if (psp) 
2133         cm_FreeSpace(psp);
2134     if (code == 0) 
2135         *outScpp = tscp;
2136     else if (tscp)
2137         cm_ReleaseSCache(tscp);
2138
2139 #ifdef DEBUG_REFCOUNT
2140     afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2141 #endif
2142     osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2143     return code;
2144 }
2145
2146 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2147  * We chase the link, and return a held pointer to the target, if it exists,
2148  * in *outScpp.  If we succeed, we return 0, otherwise we return an error code
2149  * and do not hold or return a target vnode.
2150  *
2151  * This is very similar to calling cm_NameI with the last component of a name,
2152  * which happens to be a symlink, except that we've already passed by the name.
2153  *
2154  * This function is typically called by the directory listing functions, which
2155  * encounter symlinks but need to return the proper file length so programs
2156  * like "more" work properly when they make use of the attributes retrieved from
2157  * the dir listing.
2158  *
2159  * The input vnode should not be locked when this function is called.
2160  */
2161 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2162                          cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2163 {
2164     long code;
2165     cm_space_t *spacep;
2166     cm_scache_t *newRootScp;
2167
2168     *outScpp = NULL;
2169
2170     osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2171
2172     code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2173     if (code) 
2174         return code;
2175
2176     /* now, if newRootScp is NULL, we're really being told that the symlink
2177      * is relative to the current directory (dscp).
2178      */
2179     if (newRootScp == NULL) {
2180         newRootScp = dscp;
2181         cm_HoldSCache(dscp);
2182     }
2183
2184     code = cm_NameI(newRootScp, spacep->wdata,
2185                     CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2186                     userp, NULL, reqp, outScpp);
2187
2188     if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2189         code = CM_ERROR_NOSUCHPATH;
2190
2191     /* this stuff is allocated no matter what happened on the namei call,
2192      * so free it */
2193     cm_FreeSpace(spacep);
2194     cm_ReleaseSCache(newRootScp);
2195
2196     if (linkScp == *outScpp) {
2197         cm_ReleaseSCache(*outScpp);
2198         *outScpp = NULL;
2199         code = CM_ERROR_NOSUCHPATH;
2200     }
2201
2202     return code;
2203 }
2204
2205 /* for a given entry, make sure that it isn't in the stat cache, and then
2206  * add it to the list of file IDs to be obtained.
2207  *
2208  * Don't bother adding it if we already have a vnode.  Note that the dir
2209  * is locked, so we have to be careful checking the vnode we're thinking of
2210  * processing, to avoid deadlocks.
2211  */
2212 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2213                      osi_hyper_t *offp)
2214 {
2215     osi_hyper_t thyper;
2216     cm_bulkStat_t *bsp;
2217     int i;
2218     cm_scache_t *tscp;
2219     cm_fid_t tfid;
2220
2221     bsp = rockp;
2222
2223     /* Don't overflow bsp. */
2224     if (bsp->counter >= CM_BULKMAX)
2225         return CM_ERROR_STOPNOW;
2226
2227     thyper.LowPart = cm_data.buf_blockSize;
2228     thyper.HighPart = 0;
2229     thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2230
2231     /* thyper is now the first byte past the end of the record we're
2232      * interested in, and bsp->bufOffset is the first byte of the record
2233      * we're interested in.
2234      * Skip data in the others.
2235      * Skip '.' and '..'
2236      */
2237     if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2238         return 0;
2239     if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2240         return CM_ERROR_STOPNOW;
2241     if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2242         return 0;
2243
2244     cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2245     tscp = cm_FindSCache(&tfid);
2246     if (tscp) {
2247         if (lock_TryWrite(&tscp->rw)) {
2248             /* we have an entry that we can look at */
2249             if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2250                 /* we have a callback on it.  Don't bother
2251                  * fetching this stat entry, since we're happy
2252                  * with the info we have.
2253                  */
2254                 lock_ReleaseWrite(&tscp->rw);
2255                 cm_ReleaseSCache(tscp);
2256                 return 0;
2257             }
2258             lock_ReleaseWrite(&tscp->rw);
2259         }       /* got lock */
2260         cm_ReleaseSCache(tscp);
2261     }   /* found entry */
2262
2263 #ifdef AFS_FREELANCE_CLIENT
2264     // yj: if this is a mountpoint under root.afs then we don't want it
2265     // to be bulkstat-ed, instead, we call getSCache directly and under
2266     // getSCache, it is handled specially.
2267     if  ( cm_freelanceEnabled &&
2268           tfid.cell==AFS_FAKE_ROOT_CELL_ID && 
2269           tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2270           !(tfid.vnode==0x1 && tfid.unique==0x1) )
2271     {       
2272         osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2273         return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2274     }
2275 #endif /* AFS_FREELANCE_CLIENT */
2276
2277     i = bsp->counter++;
2278     bsp->fids[i].Volume = scp->fid.volume;
2279     bsp->fids[i].Vnode = tfid.vnode;
2280     bsp->fids[i].Unique = tfid.unique;
2281     return 0;
2282 }       
2283
2284 afs_int32
2285 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2286 {
2287     afs_int32 code = 0;
2288     AFSCBFids fidStruct;
2289     AFSBulkStats statStruct;
2290     cm_conn_t *connp;
2291     AFSCBs callbackStruct;
2292     long filex;
2293     AFSVolSync volSync;
2294     cm_callbackRequest_t cbReq;
2295     long filesThisCall;
2296     long i;
2297     long j;
2298     cm_scache_t *scp;
2299     cm_fid_t tfid;
2300     struct rx_connection * rxconnp;
2301     int inlinebulk = 0;         /* Did we use InlineBulkStatus RPC or not? */
2302         
2303     memset(&volSync, 0, sizeof(volSync));
2304
2305     /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2306      * make the calls to create the entries.  Handle AFSCBMAX files at a
2307      * time.
2308      */
2309     for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2310         filesThisCall = bbp->counter - filex;
2311         if (filesThisCall > AFSCBMAX) 
2312             filesThisCall = AFSCBMAX;
2313
2314         fidStruct.AFSCBFids_len = filesThisCall;
2315         fidStruct.AFSCBFids_val = &bbp->fids[filex];
2316         statStruct.AFSBulkStats_len = filesThisCall;
2317         statStruct.AFSBulkStats_val = &bbp->stats[filex];
2318         callbackStruct.AFSCBs_len = filesThisCall;
2319         callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2320         cm_StartCallbackGrantingCall(NULL, &cbReq);
2321         osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2322         do {
2323             code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2324             if (code) 
2325                 continue;
2326
2327             rxconnp = cm_GetRxConn(connp);
2328             if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2329                 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2330                                      &statStruct, &callbackStruct, &volSync);
2331                 if (code == RXGEN_OPCODE) {
2332                     cm_SetServerNoInlineBulk(connp->serverp, 0);
2333                 } else {
2334                     inlinebulk = 1;
2335                 }
2336             }
2337             if (!inlinebulk) {
2338                 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2339                                         &statStruct, &callbackStruct, &volSync);
2340             }
2341             rx_PutConnection(rxconnp);
2342
2343         } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2344                              &volSync, NULL, &cbReq, code));
2345         code = cm_MapRPCError(code, reqp);
2346
2347         /* may as well quit on an error, since we're not going to do
2348          * much better on the next immediate call, either.
2349          */
2350         if (code) {
2351             osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2352                       inlinebulk ? "Inline" : "", code);
2353             cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2354             break;
2355         } else {
2356             osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2357         }
2358
2359         /* otherwise, we should do the merges */
2360         for (i = 0; i<filesThisCall; i++) {
2361             j = filex + i;
2362             cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2363             code = cm_GetSCache(&tfid, &scp, userp, reqp);
2364             if (code != 0) 
2365                 continue;
2366
2367             /* otherwise, if this entry has no callback info, 
2368              * merge in this.
2369              */
2370             lock_ObtainWrite(&scp->rw);
2371             /* now, we have to be extra paranoid on merging in this
2372              * information, since we didn't use cm_SyncOp before
2373              * starting the fetch to make sure that no bad races
2374              * were occurring.  Specifically, we need to make sure
2375              * we don't obliterate any newer information in the
2376              * vnode than have here.
2377              *
2378              * Right now, be pretty conservative: if there's a
2379              * callback or a pending call, skip it.
2380              * However, if the prior attempt to obtain status
2381              * was refused access or the volume is .readonly,
2382              * take the data in any case since we have nothing
2383              * better for the in flight directory enumeration that
2384              * resulted in this function being called.
2385              */
2386             if ((scp->cbServerp == NULL &&
2387                 !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2388                 (scp->flags & CM_SCACHEFLAG_PURERO) ||
2389                 (scp->flags & CM_SCACHEFLAG_EACCESS)) {
2390                 cm_EndCallbackGrantingCall(scp, &cbReq,
2391                                             &bbp->callbacks[j],
2392                                             &volSync,
2393                                             CM_CALLBACK_MAINTAINCOUNT);
2394                 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2395             }       
2396             lock_ReleaseWrite(&scp->rw);
2397             cm_ReleaseSCache(scp);
2398         } /* all files in the response */
2399         /* now tell it to drop the count,
2400          * after doing the vnode processing above */
2401         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2402     }   /* while there are still more files to process */
2403
2404     /* If we did the InlineBulk RPC pull out the return code and log it */
2405     if (inlinebulk) {
2406         if ((&bbp->stats[0])->errorCode) {
2407             osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d", 
2408                      (&bbp->stats[0])->errorCode);
2409             code = (&bbp->stats[0])->errorCode;
2410         }
2411     }
2412
2413     return code;
2414 }
2415
2416 /* called with a write locked scp and a pointer to a buffer.  Make bulk stat
2417  * calls on all undeleted files in the page of the directory specified.
2418  */
2419 afs_int32
2420 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2421                cm_req_t *reqp)
2422 {
2423     afs_int32 code;
2424     cm_bulkStat_t *bbp;
2425
2426     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2427
2428     /* should be on a buffer boundary */
2429     osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2430
2431     bbp = malloc(sizeof(cm_bulkStat_t));
2432     memset(bbp, 0, sizeof(cm_bulkStat_t));
2433     bbp->bufOffset = *offsetp;
2434
2435     lock_ReleaseWrite(&dscp->rw);
2436     /* first, assemble the file IDs we need to stat */
2437     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2438
2439     /* if we failed, bail out early */
2440     if (code && code != CM_ERROR_STOPNOW) {
2441         free(bbp);
2442         lock_ObtainWrite(&dscp->rw);
2443         return code;
2444     }
2445
2446     code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2447     osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2448
2449     lock_ObtainWrite(&dscp->rw);
2450     free(bbp);
2451     return 0;
2452 }       
2453
2454 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2455 {
2456     long mask;
2457
2458     /* initialize store back mask as inexpensive local variable */
2459     mask = 0;
2460     memset(statusp, 0, sizeof(AFSStoreStatus));
2461
2462     /* copy out queued info from scache first, if scp passed in */
2463     if (scp) {
2464         if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2465             statusp->ClientModTime = scp->clientModTime;
2466             mask |= AFS_SETMODTIME;
2467             scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2468         }
2469     }
2470
2471     if (attrp) {
2472         /* now add in our locally generated request */
2473         if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2474             statusp->ClientModTime = attrp->clientModTime;
2475             mask |= AFS_SETMODTIME;
2476         }
2477         if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2478             statusp->UnixModeBits = attrp->unixModeBits;
2479             mask |= AFS_SETMODE;
2480         }
2481         if (attrp->mask & CM_ATTRMASK_OWNER) {
2482             statusp->Owner = attrp->owner;
2483             mask |= AFS_SETOWNER;
2484         }
2485         if (attrp->mask & CM_ATTRMASK_GROUP) {
2486             statusp->Group = attrp->group;
2487             mask |= AFS_SETGROUP;
2488         }
2489     }
2490     statusp->Mask = mask;
2491 }       
2492
2493 /* set the file size, and make sure that all relevant buffers have been
2494  * truncated.  Ensure that any partially truncated buffers have been zeroed
2495  * to the end of the buffer.
2496  */
2497 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2498                    cm_req_t *reqp)
2499 {
2500     long code;
2501     int shrinking;
2502
2503     /* start by locking out buffer creation */
2504     lock_ObtainWrite(&scp->bufCreateLock);
2505
2506     /* verify that this is a file, not a dir or a symlink */
2507     lock_ObtainWrite(&scp->rw);
2508     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2509                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2510     if (code) 
2511         goto done;
2512     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2513
2514     if (scp->fileType != CM_SCACHETYPE_FILE) {
2515         code = CM_ERROR_ISDIR;
2516         goto done;
2517     }
2518
2519   startover:
2520     if (LargeIntegerLessThan(*sizep, scp->length))
2521         shrinking = 1;
2522     else
2523         shrinking = 0;
2524
2525     lock_ReleaseWrite(&scp->rw);
2526
2527     /* can't hold scp->rw lock here, since we may wait for a storeback to
2528      * finish if the buffer package is cleaning a buffer by storing it to
2529      * the server.
2530      */
2531     if (shrinking)
2532         buf_Truncate(scp, userp, reqp, sizep);
2533
2534     /* now ensure that file length is short enough, and update truncPos */
2535     lock_ObtainWrite(&scp->rw);
2536
2537     /* make sure we have a callback (so we have the right value for the
2538      * length), and wait for it to be safe to do a truncate.
2539      */
2540     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2541                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2542                       | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2543
2544     /* If we only have 'i' bits, then we should still be able to set
2545        the size of a file we created. */
2546     if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2547         code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2548                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2549                          | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2550     }
2551
2552     if (code) 
2553         goto done;
2554
2555     if (LargeIntegerLessThan(*sizep, scp->length)) {
2556         /* a real truncation.  If truncPos is not set yet, or is bigger
2557          * than where we're truncating the file, set truncPos to this
2558          * new value.
2559          */
2560         if (!shrinking)
2561             goto startover;
2562         if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2563              || LargeIntegerLessThan(*sizep, scp->length)) {
2564             /* set trunc pos */
2565             scp->truncPos = *sizep;
2566             scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2567         }
2568         /* in either case, the new file size has been changed */
2569         scp->length = *sizep;
2570         scp->mask |= CM_SCACHEMASK_LENGTH;
2571     }
2572     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2573         /* really extending the file */
2574         scp->length = *sizep;
2575         scp->mask |= CM_SCACHEMASK_LENGTH;
2576     }
2577
2578     /* done successfully */
2579     code = 0;
2580
2581     cm_SyncOpDone(scp, NULL, 
2582                    CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2583                    | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2584
2585   done:
2586     lock_ReleaseWrite(&scp->rw);
2587     lock_ReleaseWrite(&scp->bufCreateLock);
2588
2589     return code;
2590 }
2591
2592 /* set the file size or other attributes (but not both at once) */
2593 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2594                 cm_req_t *reqp)
2595 {
2596     long code;
2597     AFSFetchStatus afsOutStatus;
2598     AFSVolSync volSync;
2599     cm_conn_t *connp;
2600     AFSFid tfid;
2601     AFSStoreStatus afsInStatus;
2602     struct rx_connection * rxconnp;
2603
2604     memset(&volSync, 0, sizeof(volSync));
2605
2606     /* handle file length setting */
2607     if (attrp->mask & CM_ATTRMASK_LENGTH)
2608         return cm_SetLength(scp, &attrp->length, userp, reqp);
2609
2610     lock_ObtainWrite(&scp->rw);
2611     /* otherwise, we have to make an RPC to get the status */
2612     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2613     if (code) {
2614         lock_ReleaseWrite(&scp->rw);
2615         return code;
2616     }
2617     lock_ConvertWToR(&scp->rw);
2618
2619     /* make the attr structure */
2620     cm_StatusFromAttr(&afsInStatus, scp, attrp);
2621
2622     tfid.Volume = scp->fid.volume;
2623     tfid.Vnode = scp->fid.vnode;
2624     tfid.Unique = scp->fid.unique;
2625     lock_ReleaseRead(&scp->rw);
2626
2627     /* now make the RPC */
2628     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2629     do {
2630         code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2631         if (code) 
2632             continue;
2633
2634         rxconnp = cm_GetRxConn(connp);
2635         code = RXAFS_StoreStatus(rxconnp, &tfid,
2636                                   &afsInStatus, &afsOutStatus, &volSync);
2637         rx_PutConnection(rxconnp);
2638
2639     } while (cm_Analyze(connp, userp, reqp,
2640                          &scp->fid, &volSync, NULL, NULL, code));
2641     code = cm_MapRPCError(code, reqp);
2642
2643     if (code)
2644         osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2645     else
2646         osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2647
2648     lock_ObtainWrite(&scp->rw);
2649     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2650     if (code == 0)
2651         cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2652                         CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2653         
2654     /* if we're changing the mode bits, discard the ACL cache, 
2655      * since we changed the mode bits.
2656      */
2657     if (afsInStatus.Mask & AFS_SETMODE) 
2658         cm_FreeAllACLEnts(scp);
2659     lock_ReleaseWrite(&scp->rw);
2660     return code;
2661 }       
2662
2663 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2664                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2665 {       
2666     cm_conn_t *connp;
2667     long code;
2668     AFSFid dirAFSFid;
2669     cm_callbackRequest_t cbReq;
2670     AFSFid newAFSFid;
2671     cm_fid_t newFid;
2672     cm_scache_t *scp = NULL;
2673     int didEnd;
2674     AFSStoreStatus inStatus;
2675     AFSFetchStatus updatedDirStatus;
2676     AFSFetchStatus newFileStatus;
2677     AFSCallBack newFileCallback;
2678     AFSVolSync volSync;
2679     struct rx_connection * rxconnp;
2680     cm_dirOp_t dirop;
2681     fschar_t * fnamep = NULL;
2682
2683     memset(&volSync, 0, sizeof(volSync));
2684
2685     /* can't create names with @sys in them; must expand it manually first.
2686      * return "invalid request" if they try.
2687      */
2688     if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2689         return CM_ERROR_ATSYS;
2690     }
2691
2692 #ifdef AFS_FREELANCE_CLIENT
2693     /* Freelance root volume does not hold files */
2694     if (cm_freelanceEnabled &&
2695         dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2696         dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2697     {
2698         return CM_ERROR_NOACCESS;
2699     }
2700 #endif /* AFS_FREELANCE_CLIENT */
2701
2702     /* before starting the RPC, mark that we're changing the file data, so
2703      * that someone who does a chmod will know to wait until our call
2704      * completes.
2705      */
2706     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2707     lock_ObtainWrite(&dscp->rw);
2708     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2709     lock_ReleaseWrite(&dscp->rw);
2710     if (code == 0) {
2711         cm_StartCallbackGrantingCall(NULL, &cbReq);
2712     } else {
2713         cm_EndDirOp(&dirop);
2714     }
2715     if (code) {
2716         return code;
2717     }
2718     didEnd = 0;
2719
2720     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2721
2722     cm_StatusFromAttr(&inStatus, NULL, attrp);
2723
2724     /* try the RPC now */
2725     osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2726     do {
2727         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2728         if (code) 
2729             continue;
2730
2731         dirAFSFid.Volume = dscp->fid.volume;
2732         dirAFSFid.Vnode = dscp->fid.vnode;
2733         dirAFSFid.Unique = dscp->fid.unique;
2734
2735         rxconnp = cm_GetRxConn(connp);
2736         code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2737                                  &inStatus, &newAFSFid, &newFileStatus,
2738                                  &updatedDirStatus, &newFileCallback,
2739                                  &volSync);
2740         rx_PutConnection(rxconnp);
2741
2742     } while (cm_Analyze(connp, userp, reqp,
2743                          &dscp->fid, &volSync, NULL, &cbReq, code));
2744     code = cm_MapRPCError(code, reqp);
2745         
2746     if (code)
2747         osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2748     else
2749         osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2750
2751     if (dirop.scp) {
2752         lock_ObtainWrite(&dirop.scp->dirlock);
2753         dirop.lockType = CM_DIRLOCK_WRITE;
2754     }
2755     lock_ObtainWrite(&dscp->rw);
2756     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2757     if (code == 0) {
2758         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2759     }
2760     lock_ReleaseWrite(&dscp->rw);
2761
2762     /* now try to create the file's entry, too, but be careful to 
2763      * make sure that we don't merge in old info.  Since we weren't locking
2764      * out any requests during the file's creation, we may have pretty old
2765      * info.
2766      */
2767     if (code == 0) {
2768         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2769         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2770         if (code == 0) {
2771             lock_ObtainWrite(&scp->rw);
2772             scp->creator = userp;               /* remember who created it */
2773             if (!cm_HaveCallback(scp)) {
2774                 cm_EndCallbackGrantingCall(scp, &cbReq,
2775                                            &newFileCallback, &volSync, 0);
2776                 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2777                                userp, reqp, 0);
2778                 didEnd = 1;     
2779             }       
2780             lock_ReleaseWrite(&scp->rw);
2781         }
2782     }
2783
2784     /* make sure we end things properly */
2785     if (!didEnd)
2786         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2787
2788     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2789         cm_DirCreateEntry(&dirop, fnamep, &newFid);
2790 #ifdef USE_BPLUS
2791         cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2792 #endif
2793     }
2794     cm_EndDirOp(&dirop);
2795
2796     if (fnamep)
2797         free(fnamep);
2798
2799     if (scp) {
2800         if (scpp)
2801             *scpp = scp;
2802         else
2803             cm_ReleaseSCache(scp);
2804     }
2805     return code;
2806 }       
2807
2808 /*
2809  * locked if TRUE means write-locked
2810  * else the cm_scache_t rw must not be held
2811  */
2812 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
2813 {
2814     long code;
2815
2816     if (locked)
2817         lock_ReleaseWrite(&scp->rw);
2818     code = buf_CleanVnode(scp, userp, reqp);
2819     if (code == 0) {
2820         lock_ObtainWrite(&scp->rw);
2821
2822         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2823                           | CM_SCACHEMASK_CLIENTMODTIME
2824                           | CM_SCACHEMASK_LENGTH))
2825             code = cm_StoreMini(scp, userp, reqp);
2826
2827         if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2828             code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2829             scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2830         }
2831
2832         if (!locked)
2833             lock_ReleaseWrite(&scp->rw);
2834     } else if (locked) {
2835         lock_ObtainWrite(&scp->rw);
2836     }
2837     return code;
2838 }
2839
2840 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2841                 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2842 {
2843     cm_conn_t *connp;
2844     long code;
2845     AFSFid dirAFSFid;
2846     cm_callbackRequest_t cbReq;
2847     AFSFid newAFSFid;
2848     cm_fid_t newFid;
2849     cm_scache_t *scp = NULL;
2850     int didEnd;
2851     AFSStoreStatus inStatus;
2852     AFSFetchStatus updatedDirStatus;
2853     AFSFetchStatus newDirStatus;
2854     AFSCallBack newDirCallback;
2855     AFSVolSync volSync;
2856     struct rx_connection * rxconnp;
2857     cm_dirOp_t dirop;
2858     fschar_t * fnamep = NULL;
2859
2860     memset(&volSync, 0, sizeof(volSync));
2861
2862     /* can't create names with @sys in them; must expand it manually first.
2863      * return "invalid request" if they try.
2864      */
2865     if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2866         return CM_ERROR_ATSYS;
2867     }
2868
2869 #ifdef AFS_FREELANCE_CLIENT
2870     /* Freelance root volume does not hold subdirectories */
2871     if (cm_freelanceEnabled &&
2872         dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2873         dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2874     {
2875         return CM_ERROR_NOACCESS;
2876     }
2877 #endif /* AFS_FREELANCE_CLIENT */
2878
2879     /* before starting the RPC, mark that we're changing the directory
2880      * data, so that someone who does a chmod on the dir will wait until
2881      * our call completes.
2882      */
2883     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2884     lock_ObtainWrite(&dscp->rw);
2885     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2886     lock_ReleaseWrite(&dscp->rw);
2887     if (code == 0) {
2888         cm_StartCallbackGrantingCall(NULL, &cbReq);
2889     } else {
2890         cm_EndDirOp(&dirop);
2891     }
2892     if (code) {
2893         return code;
2894     }
2895     didEnd = 0;
2896
2897     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2898     cm_StatusFromAttr(&inStatus, NULL, attrp);
2899
2900     /* try the RPC now */
2901     osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2902     do {
2903         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2904         if (code) 
2905             continue;
2906
2907         dirAFSFid.Volume = dscp->fid.volume;
2908         dirAFSFid.Vnode = dscp->fid.vnode;
2909         dirAFSFid.Unique = dscp->fid.unique;
2910
2911         rxconnp = cm_GetRxConn(connp);
2912         code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
2913                               &inStatus, &newAFSFid, &newDirStatus,
2914                               &updatedDirStatus, &newDirCallback,
2915                               &volSync);
2916         rx_PutConnection(rxconnp);
2917
2918     } while (cm_Analyze(connp, userp, reqp,
2919                         &dscp->fid, &volSync, NULL, &cbReq, code));
2920     code = cm_MapRPCError(code, reqp);
2921         
2922     if (code)
2923         osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2924     else
2925         osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2926
2927     if (dirop.scp) {
2928         lock_ObtainWrite(&dirop.scp->dirlock);
2929         dirop.lockType = CM_DIRLOCK_WRITE;
2930     }
2931     lock_ObtainWrite(&dscp->rw);
2932     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2933     if (code == 0) {
2934         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2935     }
2936     lock_ReleaseWrite(&dscp->rw);
2937
2938     /* now try to create the new dir's entry, too, but be careful to 
2939      * make sure that we don't merge in old info.  Since we weren't locking
2940      * out any requests during the file's creation, we may have pretty old
2941      * info.
2942      */
2943     if (code == 0) {
2944         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2945         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2946         if (code == 0) {
2947             lock_ObtainWrite(&scp->rw);
2948             if (!cm_HaveCallback(scp)) {
2949                 cm_EndCallbackGrantingCall(scp, &cbReq,
2950                                             &newDirCallback, &volSync, 0);
2951                 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2952                                 userp, reqp, 0);
2953                 didEnd = 1;             
2954             }
2955             lock_ReleaseWrite(&scp->rw);
2956         }
2957     }
2958
2959     /* make sure we end things properly */
2960     if (!didEnd)
2961         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2962
2963     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2964         cm_DirCreateEntry(&dirop, fnamep, &newFid);
2965 #ifdef USE_BPLUS
2966         cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2967 #endif
2968     }
2969     cm_EndDirOp(&dirop);
2970
2971     free(fnamep);
2972
2973     if (scp) {
2974         if (scpp)
2975             *scpp = scp;
2976         else
2977             cm_ReleaseSCache(scp);
2978     }
2979
2980     /* and return error code */
2981     return code;
2982 }       
2983
2984 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
2985              cm_user_t *userp, cm_req_t *reqp)
2986 {
2987     cm_conn_t *connp;
2988     long code = 0;
2989     AFSFid dirAFSFid;
2990     AFSFid existingAFSFid;
2991     AFSFetchStatus updatedDirStatus;
2992     AFSFetchStatus newLinkStatus;
2993     AFSVolSync volSync;
2994     struct rx_connection * rxconnp;
2995     cm_dirOp_t dirop;
2996     fschar_t * fnamep = NULL;
2997
2998     memset(&volSync, 0, sizeof(volSync));
2999
3000     if (dscp->fid.cell != sscp->fid.cell ||
3001         dscp->fid.volume != sscp->fid.volume) {
3002         return CM_ERROR_CROSSDEVLINK;
3003     }
3004
3005     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3006     lock_ObtainWrite(&dscp->rw);
3007     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3008     lock_ReleaseWrite(&dscp->rw);
3009     if (code != 0)
3010         cm_EndDirOp(&dirop);
3011
3012     if (code)
3013         return code;
3014
3015     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3016
3017     /* try the RPC now */
3018     osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3019     do {
3020         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3021         if (code) continue;
3022
3023         dirAFSFid.Volume = dscp->fid.volume;
3024         dirAFSFid.Vnode = dscp->fid.vnode;
3025         dirAFSFid.Unique = dscp->fid.unique;
3026
3027         existingAFSFid.Volume = sscp->fid.volume;
3028         existingAFSFid.Vnode = sscp->fid.vnode;
3029         existingAFSFid.Unique = sscp->fid.unique;
3030
3031         rxconnp = cm_GetRxConn(connp);
3032         code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3033             &newLinkStatus, &updatedDirStatus, &volSync);
3034         rx_PutConnection(rxconnp);
3035         osi_Log1(afsd_logp,"  RXAFS_Link returns 0x%x", code);
3036
3037     } while (cm_Analyze(connp, userp, reqp,
3038         &dscp->fid, &volSync, NULL, NULL, code));
3039
3040     code = cm_MapRPCError(code, reqp);
3041
3042     if (code)
3043         osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3044     else
3045         osi_Log0(afsd_logp, "CALL Link SUCCESS");
3046
3047     if (dirop.scp) {
3048         lock_ObtainWrite(&dirop.scp->dirlock);
3049         dirop.lockType = CM_DIRLOCK_WRITE;
3050     }
3051     lock_ObtainWrite(&dscp->rw);
3052     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3053     if (code == 0) {
3054         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3055     }
3056     lock_ReleaseWrite(&dscp->rw);
3057
3058     if (code == 0) {
3059         if (cm_CheckDirOpForSingleChange(&dirop)) {
3060             cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3061 #ifdef USE_BPLUS
3062             cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3063 #endif
3064         }
3065     }
3066     cm_EndDirOp(&dirop);
3067
3068     /* Update the linked object status */
3069     if (code == 0) {
3070         lock_ObtainWrite(&sscp->rw);
3071         cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3072         lock_ReleaseWrite(&sscp->rw);
3073     }
3074
3075     free(fnamep);
3076
3077     return code;
3078 }
3079
3080 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3081                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3082 {
3083     cm_conn_t *connp;
3084     long code;
3085     AFSFid dirAFSFid;
3086     AFSFid newAFSFid;
3087     cm_fid_t newFid;
3088     cm_scache_t *scp;
3089     AFSStoreStatus inStatus;
3090     AFSFetchStatus updatedDirStatus;
3091     AFSFetchStatus newLinkStatus;
3092     AFSVolSync volSync;
3093     struct rx_connection * rxconnp;
3094     cm_dirOp_t dirop;
3095     fschar_t *fnamep = NULL;
3096
3097     memset(&volSync, 0, sizeof(volSync));
3098
3099     /* before starting the RPC, mark that we're changing the directory data,
3100      * so that someone who does a chmod on the dir will wait until our
3101      * call completes.
3102      */
3103     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3104     lock_ObtainWrite(&dscp->rw);
3105     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3106     lock_ReleaseWrite(&dscp->rw);
3107     if (code != 0)
3108         cm_EndDirOp(&dirop);
3109     if (code) {
3110         return code;
3111     }
3112
3113     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3114
3115     cm_StatusFromAttr(&inStatus, NULL, attrp);
3116
3117     /* try the RPC now */
3118     osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3119     do {
3120         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3121         if (code) 
3122             continue;
3123
3124         dirAFSFid.Volume = dscp->fid.volume;
3125         dirAFSFid.Vnode = dscp->fid.vnode;
3126         dirAFSFid.Unique = dscp->fid.unique;
3127
3128         rxconnp = cm_GetRxConn(connp);
3129         code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3130                               &inStatus, &newAFSFid, &newLinkStatus,
3131                               &updatedDirStatus, &volSync);
3132         rx_PutConnection(rxconnp);
3133
3134     } while (cm_Analyze(connp, userp, reqp,
3135                          &dscp->fid, &volSync, NULL, NULL, code));
3136     code = cm_MapRPCError(code, reqp);
3137         
3138     if (code)
3139         osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3140     else
3141         osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3142
3143     if (dirop.scp) {
3144         lock_ObtainWrite(&dirop.scp->dirlock);
3145         dirop.lockType = CM_DIRLOCK_WRITE;
3146     }
3147     lock_ObtainWrite(&dscp->rw);
3148     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3149     if (code == 0) {
3150         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3151     }
3152     lock_ReleaseWrite(&dscp->rw);
3153
3154     if (code == 0) {
3155         if (cm_CheckDirOpForSingleChange(&dirop)) {
3156             cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3157
3158             cm_DirCreateEntry(&dirop, fnamep, &newFid);
3159 #ifdef USE_BPLUS
3160             cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3161 #endif
3162         }
3163     }
3164     cm_EndDirOp(&dirop);
3165
3166     /* now try to create the new dir's entry, too, but be careful to 
3167      * make sure that we don't merge in old info.  Since we weren't locking
3168      * out any requests during the file's creation, we may have pretty old
3169      * info.
3170      */
3171     if (code == 0) {
3172         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3173         code = cm_GetSCache(&newFid, &scp, userp, reqp);
3174         if (code == 0) {
3175             lock_ObtainWrite(&scp->rw);
3176             if (!cm_HaveCallback(scp)) {
3177                 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3178                                 userp, reqp, 0);
3179             }       
3180             lock_ReleaseWrite(&scp->rw);
3181             cm_ReleaseSCache(scp);
3182         }
3183     }
3184
3185     free(fnamep);
3186         
3187     /* and return error code */
3188     return code;
3189 }
3190
3191 /*! \brief Remove a directory
3192
3193   Encapsulates a call to RXAFS_RemoveDir().
3194
3195   \param[in] dscp cm_scache_t for the directory containing the
3196       directory to be removed.
3197
3198   \param[in] fnamep This will be the original name of the directory
3199       as known to the file server.   It will be passed in to RXAFS_RemoveDir().
3200       This parameter is optional.  If it is not provided the value
3201       will be looked up.
3202
3203   \param[in] cnamep Normalized name used to update the local
3204       directory caches.
3205
3206   \param[in] userp cm_user_t for the request.
3207
3208   \param[in] reqp Request tracker.
3209 */
3210 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3211 {
3212     cm_conn_t *connp;
3213     long code;
3214     AFSFid dirAFSFid;
3215     int didEnd;
3216     AFSFetchStatus updatedDirStatus;
3217     AFSVolSync volSync;
3218     struct rx_connection * rxconnp;
3219     cm_dirOp_t dirop;
3220     cm_scache_t *scp = NULL;
3221     int free_fnamep = FALSE;
3222
3223     memset(&volSync, 0, sizeof(volSync));
3224
3225     if (fnamep == NULL) {
3226         code = -1;
3227 #ifdef USE_BPLUS
3228         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
3229         if (code == 0) {
3230             code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3231             if (code == 0)
3232                 free_fnamep = TRUE;
3233             cm_EndDirOp(&dirop);
3234         }
3235 #endif
3236         if (code)
3237             goto done;
3238     }
3239
3240     code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3241     if (code)
3242         goto done;
3243
3244     /* before starting the RPC, mark that we're changing the directory data,
3245      * so that someone who does a chmod on the dir will wait until our
3246      * call completes.
3247      */
3248     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3249     lock_ObtainWrite(&dscp->rw);
3250     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3251     lock_ReleaseWrite(&dscp->rw);
3252     if (code) {
3253         cm_EndDirOp(&dirop);
3254         goto done;
3255     }
3256     didEnd = 0;
3257
3258     /* try the RPC now */
3259     osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3260     do {
3261         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3262         if (code) 
3263             continue;
3264
3265         dirAFSFid.Volume = dscp->fid.volume;
3266         dirAFSFid.Vnode = dscp->fid.vnode;
3267         dirAFSFid.Unique = dscp->fid.unique;
3268
3269         rxconnp = cm_GetRxConn(connp);
3270         code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3271                                &updatedDirStatus, &volSync);
3272         rx_PutConnection(rxconnp);
3273
3274     } while (cm_Analyze(connp, userp, reqp,
3275                         &dscp->fid, &volSync, NULL, NULL, code));
3276     code = cm_MapRPCErrorRmdir(code, reqp);
3277
3278     if (code)
3279         osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3280     else
3281         osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3282
3283     if (dirop.scp) {
3284         lock_ObtainWrite(&dirop.scp->dirlock);
3285         dirop.lockType = CM_DIRLOCK_WRITE;
3286     }
3287     lock_ObtainWrite(&dscp->rw);
3288     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3289     if (code == 0) {
3290         cm_dnlcRemove(dscp, cnamep); 
3291         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3292     }
3293     lock_ReleaseWrite(&dscp->rw);
3294
3295     if (code == 0) {
3296         if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3297             cm_DirDeleteEntry(&dirop, fnamep);
3298 #ifdef USE_BPLUS
3299             cm_BPlusDirDeleteEntry(&dirop, cnamep);
3300 #endif
3301         }
3302     }
3303     cm_EndDirOp(&dirop);
3304
3305     if (scp) {
3306         cm_ReleaseSCache(scp);
3307         if (code == 0) {
3308             lock_ObtainWrite(&scp->rw);
3309             scp->flags |= CM_SCACHEFLAG_DELETED;
3310             lock_ReleaseWrite(&scp->rw);
3311         }
3312     }
3313
3314   done:
3315     if (free_fnamep)
3316         free(fnamep);
3317
3318     /* and return error code */
3319     return code;
3320 }
3321
3322 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3323 {
3324     /* grab mutex on contents */
3325     lock_ObtainWrite(&scp->rw);
3326
3327     /* reset the prefetch info */
3328     scp->prefetch.base.LowPart = 0;             /* base */
3329     scp->prefetch.base.HighPart = 0;
3330     scp->prefetch.end.LowPart = 0;              /* and end */
3331     scp->prefetch.end.HighPart = 0;
3332
3333     /* release mutex on contents */
3334     lock_ReleaseWrite(&scp->rw);
3335
3336     /* we're done */
3337     return 0;
3338 }       
3339
3340 /*! \brief Rename a file or directory
3341
3342   Encapsulates a RXAFS_Rename() call.
3343
3344   \param[in] oldDscp cm_scache_t for the directory containing the old
3345       name.
3346
3347   \param[in] oldNamep The original old name known to the file server.
3348       This is the name that will be passed into the RXAFS_Rename().
3349       If it is not provided, it will be looked up.
3350
3351   \param[in] normalizedOldNamep Normalized old name.  This is used for
3352   updating local directory caches.
3353
3354   \param[in] newDscp cm_scache_t for the directory containing the new
3355   name.
3356
3357   \param[in] newNamep New name. Normalized.
3358
3359   \param[in] userp cm_user_t for the request.
3360
3361   \param[in,out] reqp Request tracker.
3362
3363 */
3364 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3365                cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3366                cm_req_t *reqp)
3367 {
3368     cm_conn_t *connp;
3369     long code;
3370     AFSFid oldDirAFSFid;
3371     AFSFid newDirAFSFid;
3372     int didEnd;
3373     AFSFetchStatus updatedOldDirStatus;
3374     AFSFetchStatus updatedNewDirStatus;
3375     AFSVolSync volSync;
3376     int oneDir;
3377     struct rx_connection * rxconnp;
3378     cm_dirOp_t oldDirOp;
3379     cm_fid_t   fileFid;
3380     int        diropCode = -1;
3381     cm_dirOp_t newDirOp;
3382     fschar_t * newNamep = NULL;
3383     int free_oldNamep = FALSE;
3384     cm_scache_t *oldScp = NULL, *newScp = NULL;
3385
3386     memset(&volSync, 0, sizeof(volSync));
3387
3388     if (cOldNamep == NULL || cNewNamep == NULL ||
3389         cm_ClientStrLen(cOldNamep) == 0 ||
3390         cm_ClientStrLen(cNewNamep) == 0)
3391         return CM_ERROR_INVAL;
3392
3393     /* 
3394      * Before we permit the operation, make sure that we do not already have
3395      * an object in the destination directory that has a case-insensitive match
3396      * for this name UNLESS the matching object is the object we are renaming.
3397      */
3398     code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3399     if (code) {
3400         osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed", 
3401                  oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3402         goto done;
3403     }
3404
3405     code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3406     if (code == 0) {
3407         /* found a matching object with the new name */
3408         if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3409             /* and they don't match so return an error */
3410             osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists", 
3411                       newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3412             code = CM_ERROR_EXISTS;
3413         }
3414         cm_ReleaseSCache(newScp);
3415         newScp = NULL;
3416     } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3417         code = CM_ERROR_EXISTS;
3418     } else {
3419         code = 0;
3420     }
3421     if (code) 
3422         goto done;
3423
3424     if (oldNamep == NULL) {
3425         code = -1;
3426 #ifdef USE_BPLUS
3427         code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ, &oldDirOp);
3428         if (code == 0) {
3429             code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3430             if (code == 0)
3431                 free_oldNamep = TRUE;
3432             cm_EndDirOp(&oldDirOp);
3433         }
3434 #endif
3435         if (code) {
3436             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed", 
3437                       oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3438             goto done;
3439         }
3440     }
3441
3442
3443     /* before starting the RPC, mark that we're changing the directory data,
3444      * so that someone who does a chmod on the dir will wait until our call
3445      * completes.  We do this in vnode order so that we don't deadlock,
3446      * which makes the code a little verbose.
3447      */
3448     if (oldDscp == newDscp) {
3449         /* check for identical names */
3450         if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3451             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL", 
3452                       oldDscp, newDscp);
3453             code = CM_ERROR_RENAME_IDENTICAL;
3454             goto done;
3455         }
3456
3457         oneDir = 1;
3458         cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3459         lock_ObtainWrite(&oldDscp->rw);
3460         cm_dnlcRemove(oldDscp, cOldNamep);
3461         cm_dnlcRemove(oldDscp, cNewNamep);
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         }
3468     }
3469     else {
3470         /* two distinct dir vnodes */
3471         oneDir = 0;
3472         if (oldDscp->fid.cell != newDscp->fid.cell ||
3473              oldDscp->fid.volume != newDscp->fid.volume) {
3474             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK", 
3475                       oldDscp, newDscp);
3476             code = CM_ERROR_CROSSDEVLINK;
3477             goto done;
3478         }
3479
3480         /* shouldn't happen that we have distinct vnodes for two
3481          * different files, but could due to deliberate attack, or
3482          * stale info.  Avoid deadlocks and quit now.
3483          */
3484         if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3485             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision", 
3486                       oldDscp, newDscp);
3487             code = CM_ERROR_CROSSDEVLINK;
3488             goto done;
3489         }
3490
3491         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3492             cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3493             lock_ObtainWrite(&oldDscp->rw);
3494             cm_dnlcRemove(oldDscp, cOldNamep);
3495             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3496                              CM_SCACHESYNC_STOREDATA);
3497             lock_ReleaseWrite(&oldDscp->rw);
3498             if (code != 0)
3499                 cm_EndDirOp(&oldDirOp);
3500             if (code == 0) {
3501                 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3502                 lock_ObtainWrite(&newDscp->rw);
3503                 cm_dnlcRemove(newDscp, cNewNamep);
3504                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3505                                  CM_SCACHESYNC_STOREDATA);
3506                 lock_ReleaseWrite(&newDscp->rw);
3507                 if (code) {
3508                     cm_EndDirOp(&newDirOp);
3509
3510                     /* cleanup first one */
3511                     lock_ObtainWrite(&oldDscp->rw);
3512                     cm_SyncOpDone(oldDscp, NULL,
3513                                    CM_SCACHESYNC_STOREDATA);
3514                     lock_ReleaseWrite(&oldDscp->rw);
3515                     cm_EndDirOp(&oldDirOp);
3516                 }       
3517             }
3518         }
3519         else {
3520             /* lock the new vnode entry first */
3521             cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3522             lock_ObtainWrite(&newDscp->rw);
3523             cm_dnlcRemove(newDscp, cNewNamep);
3524             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3525                               CM_SCACHESYNC_STOREDATA);
3526             lock_ReleaseWrite(&newDscp->rw);
3527             if (code != 0)
3528                 cm_EndDirOp(&newDirOp);
3529             if (code == 0) {
3530                 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3531                 lock_ObtainWrite(&oldDscp->rw);
3532                 cm_dnlcRemove(oldDscp, cOldNamep);
3533                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3534                                   CM_SCACHESYNC_STOREDATA);
3535                 lock_ReleaseWrite(&oldDscp->rw);
3536                 if (code != 0)
3537                     cm_EndDirOp(&oldDirOp);
3538                 if (code) {
3539                     /* cleanup first one */
3540                     lock_ObtainWrite(&newDscp->rw);
3541                     cm_SyncOpDone(newDscp, NULL,
3542                                    CM_SCACHESYNC_STOREDATA);
3543                     lock_ReleaseWrite(&newDscp->rw);
3544                     cm_EndDirOp(&newDirOp);
3545                 }       
3546             }
3547         }
3548     }   /* two distinct vnodes */
3549
3550     if (code) 
3551         goto done;
3552
3553     didEnd = 0;
3554
3555     newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3556
3557     /* try the RPC now */
3558     osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p", 
3559               oldDscp, newDscp);
3560     do {
3561         code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3562         if (code) 
3563             continue;
3564
3565         oldDirAFSFid.Volume = oldDscp->fid.volume;
3566         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3567         oldDirAFSFid.Unique = oldDscp->fid.unique;
3568         newDirAFSFid.Volume = newDscp->fid.volume;
3569         newDirAFSFid.Vnode = newDscp->fid.vnode;
3570         newDirAFSFid.Unique = newDscp->fid.unique;
3571
3572         rxconnp = cm_GetRxConn(connp);
3573         code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3574                             &newDirAFSFid, newNamep,
3575                             &updatedOldDirStatus, &updatedNewDirStatus,
3576                             &volSync);
3577         rx_PutConnection(rxconnp);
3578
3579     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3580                          &volSync, NULL, NULL, code));
3581     code = cm_MapRPCError(code, reqp);
3582         
3583     if (code)
3584         osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3585     else
3586         osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3587
3588     /* update the individual stat cache entries for the directories */
3589     if (oldDirOp.scp) {
3590         lock_ObtainWrite(&oldDirOp.scp->dirlock);
3591         oldDirOp.lockType = CM_DIRLOCK_WRITE;
3592     }
3593     lock_ObtainWrite(&oldDscp->rw);
3594     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3595
3596     if (code == 0)
3597         cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3598                        userp, reqp, CM_MERGEFLAG_DIROP);
3599     lock_ReleaseWrite(&oldDscp->rw);
3600
3601     if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3602 #ifdef USE_BPLUS
3603         diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3604         if (diropCode == CM_ERROR_INEXACT_MATCH)
3605             diropCode = 0;
3606         else if (diropCode == EINVAL)
3607 #endif
3608             diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3609
3610         if (diropCode == 0) {
3611             if (oneDir) {
3612                 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3613 #ifdef USE_BPLUS        
3614                 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3615 #endif
3616             }
3617                 
3618             if (diropCode == 0) { 
3619                 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3620 #ifdef USE_BPLUS
3621                 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3622 #endif  
3623             }
3624         }
3625     }
3626     cm_EndDirOp(&oldDirOp);
3627
3628     /* and update it for the new one, too, if necessary */
3629     if (!oneDir) {
3630         if (newDirOp.scp) {
3631             lock_ObtainWrite(&newDirOp.scp->dirlock);
3632             newDirOp.lockType = CM_DIRLOCK_WRITE;
3633         }
3634         lock_ObtainWrite(&newDscp->rw);
3635         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3636         if (code == 0)
3637             cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3638                             userp, reqp, CM_MERGEFLAG_DIROP);
3639         lock_ReleaseWrite(&newDscp->rw);
3640
3641 #if 0 
3642         /* 
3643          * The following optimization does not work.  
3644          * When the file server processed a RXAFS_Rename() request the 
3645          * FID of the object being moved between directories is not 
3646          * preserved.  The client does not know the new FID nor the 
3647          * version number of the target.  Not only can we not create
3648          * the directory entry in the new directory, but we can't 
3649          * preserve the cached data for the file.  It must be re-read
3650          * from the file server.  - jaltman, 2009/02/20
3651          */
3652         if (code == 0) {
3653             /* we only make the local change if we successfully made
3654                the change in the old directory AND there was only one
3655                change in the new directory */
3656             if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3657                 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3658 #ifdef USE_BPLUS
3659                 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3660 #endif
3661             }
3662         }
3663 #endif /* 0 */
3664         cm_EndDirOp(&newDirOp);
3665     }
3666
3667     /* 
3668      * After the rename the file server has invalidated the callbacks
3669      * on the file that was moved nor do we have a directory reference 
3670      * to it anymore.
3671      */
3672     lock_ObtainWrite(&oldScp->rw);
3673     cm_DiscardSCache(oldScp);
3674     lock_ReleaseWrite(&oldScp->rw);
3675
3676   done:
3677     if (oldScp)
3678         cm_ReleaseSCache(oldScp);
3679
3680     if (free_oldNamep)
3681         free(oldNamep);
3682
3683     free(newNamep);
3684
3685     /* and return error code */
3686     return code;
3687 }
3688
3689 /* Byte range locks:
3690
3691    The OpenAFS Windows client has to fake byte range locks given no
3692    server side support for such locks.  This is implemented as keyed
3693    byte range locks on the cache manager.
3694
3695    Keyed byte range locks:
3696
3697    Each cm_scache_t structure keeps track of a list of keyed locks.
3698    The key for a lock identifies an owner of a set of locks (referred
3699    to as a client).  Each key is represented by a value.  The set of
3700    key values used within a specific cm_scache_t structure form a
3701    namespace that has a scope of just that cm_scache_t structure.  The
3702    same key value can be used with another cm_scache_t structure and
3703    correspond to a completely different client.  However it is
3704    advantageous for the SMB or IFS layer to make sure that there is a
3705    1-1 mapping between client and keys over all cm_scache_t objects.
3706
3707    Assume a client C has key Key(C) (although, since the scope of the
3708    key is a cm_scache_t, the key can be Key(C,S), where S is the
3709    cm_scache_t.  But assume a 1-1 relation between keys and clients).
3710    A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3711    inclusive (a.k.a. [O,O+L-1]).  The function Key(x) is implemented
3712    through cm_generateKey() function for both SMB and IFS.
3713
3714    The list of locks for a cm_scache_t object S is maintained in
3715    S->fileLocks.  The cache manager will set a lock on the AFS file
3716    server in order to assert the locks in S->fileLocks.  If only
3717    shared locks are in place for S, then the cache manager will obtain
3718    a LockRead lock, while if there are any exclusive locks, it will
3719    obtain a LockWrite lock.  If the exclusive locks are all released
3720    while the shared locks remain, then the cache manager will
3721    downgrade the lock from LockWrite to LockRead.  Similarly, if an
3722    exclusive lock is obtained when only shared locks exist, then the
3723    cache manager will try to upgrade the lock from LockRead to
3724    LockWrite.
3725
3726    Each lock L owned by client C maintains a key L->key such that
3727    L->key == Key(C), the effective range defined by L->LOffset and
3728    L->LLength such that the range of bytes affected by the lock is
3729    (L->LOffset, +L->LLength), a type maintained in L->LockType which
3730    is either exclusive or shared.
3731
3732    Lock states:
3733
3734    A lock exists iff it is in S->fileLocks for some cm_scache_t
3735    S. Existing locks are in one of the following states: ACTIVE,
3736    WAITLOCK, WAITUNLOCK, LOST, DELETED.
3737
3738    The following sections describe each lock and the associated
3739    transitions.
3740
3741    1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3742       the lock with the AFS file server.  This type of lock can be
3743       exercised by a client to read or write to the locked region (as
3744       the lock allows).
3745
3746       1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3747         server lock that was required to assert the lock.  Before
3748         marking the lock as lost, the cache manager checks if the file
3749         has changed on the server.  If the file has not changed, then
3750         the cache manager will attempt to obtain a new server lock
3751         that is sufficient to assert the client side locks for the
3752         file.  If any of these fail, the lock is marked as LOST.
3753         Otherwise, it is left as ACTIVE.
3754
3755       1.2 ACTIVE->DELETED: Lock is released.
3756
3757    2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3758       grants the lock but the lock is yet to be asserted with the AFS
3759       file server.  Once the file server grants the lock, the state
3760       will transition to an ACTIVE lock.
3761
3762       2.1 WAITLOCK->ACTIVE: The server granted the lock.
3763
3764       2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3765         waiting.
3766
3767       2.3 WAITLOCK->LOST: One or more locks from this client were
3768         marked as LOST.  No further locks will be granted to this
3769         client until all lost locks are removed.
3770
3771    3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3772       receives a request for a lock that conflicts with an existing
3773       ACTIVE or WAITLOCK lock.  The lock will be placed in the queue
3774       and will be granted at such time the conflicting locks are
3775       removed, at which point the state will transition to either
3776       WAITLOCK or ACTIVE.
3777
3778       3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed.  The
3779         current serverLock is sufficient to assert this lock, or a
3780         sufficient serverLock is obtained.
3781
3782       3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3783         however the required serverLock is yet to be asserted with the
3784         server.
3785
3786       3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3787         released.
3788
3789       3.5 WAITUNLOCK->LOST: One or more locks from this client were
3790         marked as LOST.  No further locks will be granted to this
3791         client until all lost locks are removed.
3792
3793    4. LOST: A lock L is LOST if the server lock that was required to
3794       assert the lock could not be obtained or if it could not be
3795       extended, or if other locks by the same client were LOST.
3796       Essentially, once a lock is LOST, the contract between the cache
3797       manager and that specific client is no longer valid.
3798
3799       The cache manager rechecks the server lock once every minute and
3800       extends it as appropriate.  If this is not done for 5 minutes,
3801       the AFS file server will release the lock (the 5 minute timeout
3802       is based on current file server code and is fairly arbitrary).
3803       Once released, the lock cannot be re-obtained without verifying
3804       that the contents of the file hasn't been modified since the
3805       time the lock was released.  Re-obtaining the lock without
3806       verifying this may lead to data corruption.  If the lock can not
3807       be obtained safely, then all active locks for the cm_scache_t
3808       are marked as LOST.
3809
3810       4.1 LOST->DELETED: The lock is released.
3811
3812    5. DELETED: The lock is no longer relevant.  Eventually, it will
3813       get removed from the cm_scache_t. In the meantime, it will be
3814       treated as if it does not exist.
3815
3816       5.1 DELETED->not exist: The lock is removed from the
3817         cm_scache_t.
3818
3819    The following are classifications of locks based on their state.
3820
3821    6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK.  These locks
3822       have been accepted by the cache manager, but may or may not have
3823       been granted back to the client.
3824
3825    7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3826
3827    8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3828
3829    Lock operation:
3830
3831    A client C can READ range (Offset,+Length) of a file represented by
3832    cm_scache_t S iff (1):
3833
3834    1. for all _a_ in (Offset,+Length), all of the following is true:
3835
3836        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3837          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3838          shared.
3839
3840        1.2 For each LOST lock L in S->fileLocks such that _a_ in
3841          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3842          Key(C)
3843
3844        (When locks are lost on an cm_scache_t, all locks are lost.  By
3845        4.2 (below), if there is an exclusive LOST lock, then there
3846        can't be any overlapping ACTIVE locks.)
3847
3848    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3849
3850    2. for all _a_ in (Offset,+Length), one of the following is true:
3851
3852        2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3853          does not exist a LOST lock L such that _a_ in
3854          (L->LOffset,+L->LLength).
3855
3856        2.2 Byte _a_ of S is owned by C under lock L (as specified in
3857          1.2) AND L->LockType is exclusive.
3858
3859    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3860
3861    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3862       true:
3863
3864        3.1 If L->LockType is exclusive then there does NOT exist a
3865          ACCEPTED lock M in S->fileLocks such that _a_ in
3866          (M->LOffset,+M->LLength).
3867
3868          (If we count all QUEUED locks then we hit cases such as
3869          cascading waiting locks where the locks later on in the queue
3870          can be granted without compromising file integrity.  On the
3871          other hand if only ACCEPTED locks are considered, then locks
3872          that were received earlier may end up waiting for locks that
3873          were received later to be unlocked. The choice of ACCEPTED
3874          locks was made to mimic the Windows byte range lock
3875          semantics.)
3876
3877        3.2 If L->LockType is shared then for each ACCEPTED lock M in
3878          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3879          M->LockType is shared.
3880
3881    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3882
3883        4.1 M->key != Key(C)
3884
3885        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3886          and (M->LOffset,+M->LLength) do not intersect.
3887
3888          (Note: If a client loses a lock, it loses all locks.
3889          Subsequently, it will not be allowed to obtain any more locks
3890          until all existing LOST locks that belong to the client are
3891          released.  Once all locks are released by a single client,
3892          there exists no further contract between the client and AFS
3893          about the contents of the file, hence the client can then
3894          proceed to obtain new locks and establish a new contract.
3895
3896          This doesn't quite work as you think it should, because most
3897          applications aren't built to deal with losing locks they
3898          thought they once had.  For now, we don't have a good
3899          solution to lost locks.
3900
3901          Also, for consistency reasons, we have to hold off on
3902          granting locks that overlap exclusive LOST locks.)
3903
3904    A client C can only unlock locks L in S->fileLocks which have
3905    L->key == Key(C).
3906
3907    The representation and invariants are as follows:
3908
3909    - Each cm_scache_t structure keeps:
3910
3911        - A queue of byte-range locks (cm_scache_t::fileLocks) which
3912          are of type cm_file_lock_t.
3913
3914        - A record of the highest server-side lock that has been
3915          obtained for this object (cm_scache_t::serverLock), which is
3916          one of (-1), LockRead, LockWrite.
3917
3918        - A count of ACCEPTED exclusive and shared locks that are in the
3919          queue (cm_scache_t::sharedLocks and
3920          cm_scache_t::exclusiveLocks)
3921
3922    - Each cm_file_lock_t structure keeps:
3923
3924        - The type of lock (cm_file_lock_t::LockType)
3925
3926        - The key associated with the lock (cm_file_lock_t::key)
3927
3928        - The offset and length of the lock (cm_file_lock_t::LOffset
3929          and cm_file_lock_t::LLength)
3930
3931        - The state of the lock.
3932
3933        - Time of issuance or last successful extension
3934
3935    Semantic invariants:
3936
3937        I1. The number of ACCEPTED locks in S->fileLocks are
3938            (S->sharedLocks + S->exclusiveLocks)
3939
3940    External invariants:
3941
3942        I3. S->serverLock is the lock that we have asserted with the
3943            AFS file server for this cm_scache_t.
3944
3945        I4. S->serverLock == LockRead iff there is at least one ACTIVE
3946            shared lock, but no ACTIVE exclusive locks.
3947
3948        I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3949            exclusive lock.
3950
3951        I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3952            M->key == L->key IMPLIES M is LOST or DELETED.
3953
3954    --asanka
3955  */
3956
3957 #define IS_LOCK_ACTIVE(lockp)     (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3958
3959 #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)
3960
3961 #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)
3962
3963 #define IS_LOCK_LOST(lockp)       (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3964
3965 #define IS_LOCK_DELETED(lockp)    (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3966
3967 /* unsafe */
3968 #define IS_LOCK_ACCEPTED(lockp)   (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3969
3970 /* unsafe */
3971 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3972
3973 /* unsafe */
3974 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3975
3976 /* unsafe */
3977 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3978
3979 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3980 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3981 #else
3982 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3983 #endif
3984
3985 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3986
3987 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3988 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3989 #else
3990 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3991
3992 /* This should really be defined in any build that this code is being
3993    compiled. */
3994 #error  VICED_CAPABILITY_WRITELOCKACL not defined.
3995 #endif
3996
3997 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3998 {
3999     afs_int64 int_begin;
4000     afs_int64 int_end;
4001
4002     int_begin = MAX(pos->offset, neg->offset);
4003     int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
4004
4005     if (int_begin < int_end) {
4006         if (int_begin == pos->offset) {
4007             pos->length = pos->offset + pos->length - int_end;
4008             pos->offset = int_end;
4009         } else if (int_end == pos->offset + pos->length) {
4010             pos->length = int_begin - pos->offset;
4011         }
4012
4013         /* We only subtract ranges if the resulting range is
4014            contiguous.  If we try to support non-contigous ranges, we
4015            aren't actually improving performance. */
4016     }
4017 }
4018
4019 /* Called with scp->rw held.  Returns 0 if all is clear to read the
4020    specified range by the client identified by key.
4021  */
4022 long cm_LockCheckRead(cm_scache_t *scp, 
4023                       LARGE_INTEGER LOffset, 
4024                       LARGE_INTEGER LLength, 
4025                       cm_key_t key)
4026 {
4027 #ifndef ADVISORY_LOCKS
4028
4029     cm_file_lock_t *fileLock;
4030     osi_queue_t *q;
4031     long code = 0;
4032     cm_range_t range;
4033     int substract_ranges = FALSE;
4034
4035     range.offset = LOffset.QuadPart;
4036     range.length = LLength.QuadPart;
4037
4038     /*
4039
4040      1. for all _a_ in (Offset,+Length), all of the following is true:
4041
4042        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4043          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4044          shared.
4045
4046        1.2 For each LOST lock L in S->fileLocks such that _a_ in
4047          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4048          Key(C)
4049
4050     */
4051
4052     lock_ObtainRead(&cm_scacheLock);
4053
4054     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4055         fileLock = 
4056             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4057
4058