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