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