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