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