windows-afsd-symlink-20090327
[openafs.git] / src / WINNT / afsd / cm_vnodeops.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afs/param.h>
11 #include <afs/stds.h>
12
13 #include <windows.h>
14 #include <winsock2.h>
15 #include <stddef.h>
16 #include <malloc.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <errno.h>
20
21 #include <osi.h>
22
23 #include "afsd.h"
24 #include "smb.h"
25 #include "cm_btree.h"
26
27 #include <strsafe.h>
28
29 #ifdef DEBUG
30 extern void afsi_log(char *pattern, ...);
31 #endif
32
33 int cm_enableServerLocks = 1;
34
35 int cm_followBackupPath = 0;
36
37 /*
38  * Case-folding array.  This was constructed by inspecting of SMBtrace output.
39  * I do not know anything more about it.
40  */
41 unsigned char cm_foldUpper[256] = {
42      0x0,  0x1,  0x2,  0x3,  0x4,  0x5,  0x6,  0x7,
43      0x8,  0x9,  0xa,  0xb,  0xc,  0xd,  0xe,  0xf,
44     0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
45     0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
46     0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
47     0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
48     0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
49     0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
50     0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
51     0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
52     0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
53     0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
54     0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
55     0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
56     0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
57     0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
58     0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
59     0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
60     0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
61     0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
62     0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
63     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
64     0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
65     0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
66     0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
67     0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
68     0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
69     0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
70     0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
71     0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
72     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
73     0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
74 };
75
76 /*
77  * Case-insensitive string comparison.  We used to use stricmp, but it doesn't
78  * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
79  * upper-case u-umlaut).
80  */
81 int cm_stricmp(const char *str1, const char *str2)
82 {
83     char c1, c2;
84
85     while (1) {
86         if (*str1 == 0)
87             if (*str2 == 0)
88                 return 0;
89             else
90                 return -1;
91         if (*str2 == 0)
92             return 1;
93         c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
94         c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
95         if (c1 < c2)
96             return -1;
97         if (c1 > c2)
98             return 1;
99     }
100 }
101
102
103
104 /* return success if we can open this file in this mode */
105 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
106                   cm_req_t *reqp)
107 {
108     long rights;
109     long code;
110
111     rights = 0;
112     if (openMode != 1) 
113         rights |= PRSFS_READ;
114     if (openMode == 1 || openMode == 2 || trunc) 
115         rights |= PRSFS_WRITE;
116         
117     lock_ObtainWrite(&scp->rw);
118
119     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
120                       CM_SCACHESYNC_GETSTATUS
121                      | CM_SCACHESYNC_NEEDCALLBACK
122                      | CM_SCACHESYNC_LOCK);
123
124     if (code == 0 && 
125         ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
126         scp->fileType == CM_SCACHETYPE_FILE) {
127
128         cm_key_t key;
129         unsigned int sLockType;
130         LARGE_INTEGER LOffset, LLength;
131
132         /* Check if there's some sort of lock on the file at the
133            moment. */
134
135         key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
136
137         if (rights & PRSFS_WRITE)
138             sLockType = 0;
139         else
140             sLockType = LOCKING_ANDX_SHARED_LOCK;
141
142         LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
143         LOffset.LowPart  = CM_FLSHARE_OFFSET_LOW;
144         LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
145         LLength.LowPart  = CM_FLSHARE_LENGTH_LOW;
146
147         code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
148
149         if (code == 0) {
150             cm_Unlock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp);
151         } else {
152             /* In this case, we allow the file open to go through even
153                though we can't enforce mandatory locking on the
154                file. */
155             if (code == CM_ERROR_NOACCESS &&
156                 !(rights & PRSFS_WRITE))
157                 code = 0;
158             else {
159                 switch (code) {
160                 case CM_ERROR_ALLOFFLINE:
161                 case CM_ERROR_ALLDOWN:
162                 case CM_ERROR_ALLBUSY:
163                 case CM_ERROR_TIMEDOUT:
164                 case CM_ERROR_RETRY:
165                 case CM_ERROR_WOULDBLOCK:
166                     break;
167                 default:
168                     code = CM_ERROR_SHARING_VIOLATION;
169                 }
170             }
171         }
172
173     } else if (code != 0) {
174         goto _done;
175     }
176
177     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
178
179  _done:
180
181     lock_ReleaseWrite(&scp->rw);
182
183     return code;
184 }
185
186 /* return success if we can open this file in this mode */
187 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
188                     unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp, 
189                     cm_lock_data_t **ldpp)
190 {
191     long rights;
192     long code;
193
194     osi_assertx(ldpp != NULL, "null cm_lock_data_t");
195     *ldpp = NULL;
196
197     /* Always allow delete; the RPC will tell us if it's OK */
198     if (desiredAccess == DELETE)
199         return 0;
200
201     rights = 0;
202
203     if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
204         rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
205
206     /* We used to require PRSFS_WRITE if createDisp was 4
207        (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
208        However, we don't need to do that since the existence of the
209        scp implies that we don't need to create it. */
210     if (desiredAccess & AFS_ACCESS_WRITE)
211         rights |= PRSFS_WRITE;
212
213     lock_ObtainWrite(&scp->rw);
214
215     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
216                       CM_SCACHESYNC_GETSTATUS
217                      | CM_SCACHESYNC_NEEDCALLBACK
218                      | CM_SCACHESYNC_LOCK);
219
220     /*
221      * If the open will fail because the volume is readonly, then we will
222      * return an access denied error instead.  This is to help brain-dead
223      * apps run correctly on replicated volumes.
224      * See defect 10007 for more information.
225      */
226     if (code == CM_ERROR_READONLY)
227         code = CM_ERROR_NOACCESS;
228
229     if (code == 0 &&
230              ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
231              scp->fileType == CM_SCACHETYPE_FILE) {
232         cm_key_t key;
233         unsigned int sLockType;
234         LARGE_INTEGER LOffset, LLength;
235
236         /* Check if there's some sort of lock on the file at the
237            moment. */
238
239         key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
240         if (rights & PRSFS_WRITE)
241             sLockType = 0;
242         else
243             sLockType = LOCKING_ANDX_SHARED_LOCK;
244
245         /* single byte lock at offset 0x0100 0000 0000 0000 */
246         LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
247         LOffset.LowPart  = CM_FLSHARE_OFFSET_LOW;
248         LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
249         LLength.LowPart  = CM_FLSHARE_LENGTH_LOW;
250
251         code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
252
253         if (code == 0) {
254             (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
255             if (!*ldpp) {
256                 code = ENOMEM;
257                 goto _done;
258             }
259
260             (*ldpp)->key = key;
261             (*ldpp)->sLockType = sLockType;
262             (*ldpp)->LOffset.HighPart = LOffset.HighPart;
263             (*ldpp)->LOffset.LowPart = LOffset.LowPart;
264             (*ldpp)->LLength.HighPart = LLength.HighPart;
265             (*ldpp)->LLength.LowPart = LLength.LowPart;
266         } else {
267             /* In this case, we allow the file open to go through even
268                though we can't enforce mandatory locking on the
269                file. */
270             if (code == CM_ERROR_NOACCESS &&
271                 !(rights & PRSFS_WRITE))
272                 code = 0;
273             else {
274                 switch (code) {
275                 case CM_ERROR_ALLOFFLINE:
276                 case CM_ERROR_ALLDOWN:
277                 case CM_ERROR_ALLBUSY:
278                 case CM_ERROR_TIMEDOUT:
279                 case CM_ERROR_RETRY:
280                 case CM_ERROR_WOULDBLOCK:
281                     break;
282                 default:
283                     code = CM_ERROR_SHARING_VIOLATION;
284                 }
285             }
286         }
287     } else if (code != 0) {
288         goto _done;
289     }
290
291  _done:
292     lock_ReleaseWrite(&scp->rw);
293
294     osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
295     return code;
296 }
297
298 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, 
299                                cm_lock_data_t ** ldpp)
300 {
301     osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
302     lock_ObtainWrite(&scp->rw);
303     if (*ldpp) {
304         cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength, 
305                   (*ldpp)->key, 0, userp, reqp);
306         free(*ldpp);
307         *ldpp = NULL;
308     }
309     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
310     lock_ReleaseWrite(&scp->rw);
311     return 0;
312 }
313 /*
314  * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
315  * done in three steps:
316  * (1) open for deletion (NT_CREATE_AND_X)
317  * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
318  * (3) close (CLOSE)
319  * We must not do the RPC until step 3.  But if we are going to return an error
320  * code (e.g. directory not empty), we must return it by step 2, otherwise most
321  * clients will not notice it.  So we do a preliminary check.  For deleting
322  * files, this is almost free, since we have already done the RPC to get the
323  * parent directory's status bits.  But for deleting directories, we must do an
324  * additional RPC to get the directory's data to check if it is empty.  Sigh.
325  */
326 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
327         cm_req_t *reqp)
328 {
329     long code;
330     osi_hyper_t thyper;
331     cm_buf_t *bufferp;
332     cm_dirEntry_t *dep = 0;
333     unsigned short *hashTable;
334     unsigned int i, idx;
335     int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
336     int releaseLock = 0;
337
338     /* First check permissions */
339     lock_ObtainWrite(&scp->rw);
340     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
341                       CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
342     if (!code)
343         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
344     lock_ReleaseWrite(&scp->rw);
345     if (code)
346         return code;
347
348     /* If deleting directory, must be empty */
349
350     if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
351         return code;
352
353     thyper.HighPart = 0; thyper.LowPart = 0;
354     code = buf_Get(scp, &thyper, &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) {   /* 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                         if (fid_count == MAX_FID_COUNT) {
1936                             code = CM_ERROR_TOO_MANY_SYMLINKS;
1937                             cm_ReleaseSCache(nscp);
1938                             nscp = NULL;
1939                             break;
1940                         }       
1941                         fids[fid_count++] = nscp->fid;
1942                     }
1943                 }
1944
1945                 if (code) {
1946                     cm_ReleaseSCache(tscp);
1947                     if (dirScp)
1948                         cm_ReleaseSCache(dirScp);
1949                     if (psp) 
1950                         cm_FreeSpace(psp);
1951                     if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) && 
1952                         tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1953                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1954                         return CM_ERROR_NOSUCHPATH;
1955                     } else {
1956                         osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1957                         return code;
1958                     }
1959                 }
1960
1961                 haveComponent = 0;      /* component done */
1962                 if (dirScp)
1963                     cm_ReleaseSCache(dirScp);
1964                 dirScp = tscp;          /* for some symlinks */
1965                 tscp = nscp;            /* already held */
1966                 nscp = NULL;
1967                 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1968                     code = 0;
1969                     if (dirScp) {
1970                         cm_ReleaseSCache(dirScp);
1971                         dirScp = NULL;
1972                     }
1973                     break;
1974                 }
1975
1976                 /* now, if tscp is a symlink, we should follow it and
1977                  * assemble the path again.
1978                  */
1979                 lock_ObtainWrite(&tscp->rw);
1980                 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1981                                   CM_SCACHESYNC_GETSTATUS
1982                                   | CM_SCACHESYNC_NEEDCALLBACK);
1983                 if (code) {
1984                     lock_ReleaseWrite(&tscp->rw);
1985                     cm_ReleaseSCache(tscp);
1986                     tscp = NULL;
1987                     if (dirScp) {
1988                         cm_ReleaseSCache(dirScp);
1989                         dirScp = NULL;
1990                     }
1991                     break;
1992                 }
1993                 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1994
1995                 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1996                     /* this is a symlink; assemble a new buffer */
1997                     lock_ReleaseWrite(&tscp->rw);
1998                     if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1999                         cm_ReleaseSCache(tscp);
2000                         tscp = NULL;
2001                         if (dirScp) {
2002                             cm_ReleaseSCache(dirScp);
2003                             dirScp = NULL;
2004                         }
2005                         if (psp) 
2006                             cm_FreeSpace(psp);
2007                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2008                         return CM_ERROR_TOO_MANY_SYMLINKS;
2009                     }
2010                     if (tc == 0) 
2011                         restp = _C("");
2012                     else 
2013                         restp = tp;
2014
2015                     {
2016                         fschar_t * frestp;
2017
2018                         /* TODO: make this better */
2019                         frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2020                         code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2021                         free(frestp);
2022                     }
2023
2024                     if (code == 0 && linkScp != NULL) {
2025                         if (linkScp == cm_data.rootSCachep) 
2026                             fid_count = 0;
2027                         else {
2028                             for ( i=0; i<fid_count; i++) {
2029                                 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2030                                     code = CM_ERROR_TOO_MANY_SYMLINKS;
2031                                     cm_ReleaseSCache(linkScp);
2032                                     nscp = NULL;
2033                                     break;
2034                                 }
2035                             }
2036                         }
2037                         if (i == fid_count && fid_count < MAX_FID_COUNT) {
2038                             fids[fid_count++] = linkScp->fid;
2039                         }
2040                     }
2041
2042                     if (code) {
2043                         /* something went wrong */
2044                         cm_ReleaseSCache(tscp);
2045                         tscp = NULL;
2046                         if (dirScp) {
2047                             cm_ReleaseSCache(dirScp);
2048                             dirScp = NULL;
2049                         }
2050                         break;
2051                     }
2052
2053                     /* otherwise, tempsp has the new path,
2054                      * and linkScp is the new root from
2055                      * which to interpret that path.
2056                      * Continue with the namei processing,
2057                      * also doing the bookkeeping for the
2058                      * space allocation and tracking the
2059                      * vnode reference counts.
2060                      */
2061                     if (psp) 
2062                         cm_FreeSpace(psp);
2063                     psp = tempsp;
2064                     tp = psp->wdata;
2065                     cm_ReleaseSCache(tscp);
2066                     tscp = linkScp;
2067                     linkScp = NULL;
2068                     /* already held
2069                      * by AssembleLink
2070                      * now, if linkScp is null, that's
2071                      * AssembleLink's way of telling us that
2072                      * the sym link is relative to the dir
2073                      * containing the link.  We have a ref
2074                      * to it in dirScp, and we hold it now
2075                      * and reuse it as the new spot in the
2076                      * dir hierarchy.
2077                      */
2078                     if (tscp == NULL) {
2079                         tscp = dirScp;
2080                         dirScp = NULL;
2081                     }
2082                 } else {
2083                     /* not a symlink, we may be done */
2084                     lock_ReleaseWrite(&tscp->rw);
2085                     if (tc == 0) {
2086                         if (phase == 1) {
2087                             phase = 2;
2088                             tp = pathp;
2089                             continue;
2090                         }
2091                         if (dirScp) {
2092                             cm_ReleaseSCache(dirScp);
2093                             dirScp = NULL;
2094                         }
2095                         code = 0;
2096                         break;
2097                     }
2098                 }
2099                 if (dirScp) {
2100                     cm_ReleaseSCache(dirScp);
2101                     dirScp = NULL;
2102                 }
2103             } /* end of a component */
2104             else 
2105                 *cp++ = tc;
2106         } /* we have a component */
2107     } /* big while loop over all components */
2108
2109     /* already held */
2110     if (dirScp)
2111         cm_ReleaseSCache(dirScp);
2112     if (psp) 
2113         cm_FreeSpace(psp);
2114     if (code == 0) 
2115         *outScpp = tscp;
2116     else if (tscp)
2117         cm_ReleaseSCache(tscp);
2118
2119 #ifdef DEBUG_REFCOUNT
2120     afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2121 #endif
2122     osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2123     return code;
2124 }
2125
2126 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2127  * We chase the link, and return a held pointer to the target, if it exists,
2128  * in *outScpp.  If we succeed, we return 0, otherwise we return an error code
2129  * and do not hold or return a target vnode.
2130  *
2131  * This is very similar to calling cm_NameI with the last component of a name,
2132  * which happens to be a symlink, except that we've already passed by the name.
2133  *
2134  * This function is typically called by the directory listing functions, which
2135  * encounter symlinks but need to return the proper file length so programs
2136  * like "more" work properly when they make use of the attributes retrieved from
2137  * the dir listing.
2138  *
2139  * The input vnode should not be locked when this function is called.
2140  */
2141 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2142                          cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2143 {
2144     long code;
2145     cm_space_t *spacep;
2146     cm_scache_t *newRootScp;
2147
2148     *outScpp = NULL;
2149
2150     osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2151
2152     code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2153     if (code) 
2154         return code;
2155
2156     /* now, if newRootScp is NULL, we're really being told that the symlink
2157      * is relative to the current directory (dscp).
2158      */
2159     if (newRootScp == NULL) {
2160         newRootScp = dscp;
2161         cm_HoldSCache(dscp);
2162     }
2163
2164     code = cm_NameI(newRootScp, spacep->wdata,
2165                     CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2166                     userp, NULL, reqp, outScpp);
2167
2168     if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2169         code = CM_ERROR_NOSUCHPATH;
2170
2171     /* this stuff is allocated no matter what happened on the namei call,
2172      * so free it */
2173     cm_FreeSpace(spacep);
2174     cm_ReleaseSCache(newRootScp);
2175
2176     if (linkScp == *outScpp) {
2177         cm_ReleaseSCache(*outScpp);
2178         *outScpp = NULL;
2179         code = CM_ERROR_NOSUCHPATH;
2180     }
2181
2182     return code;
2183 }
2184
2185 /* for a given entry, make sure that it isn't in the stat cache, and then
2186  * add it to the list of file IDs to be obtained.
2187  *
2188  * Don't bother adding it if we already have a vnode.  Note that the dir
2189  * is locked, so we have to be careful checking the vnode we're thinking of
2190  * processing, to avoid deadlocks.
2191  */
2192 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2193                      osi_hyper_t *offp)
2194 {
2195     osi_hyper_t thyper;
2196     cm_bulkStat_t *bsp;
2197     int i;
2198     cm_scache_t *tscp;
2199     cm_fid_t tfid;
2200
2201     bsp = rockp;
2202
2203     /* Don't overflow bsp. */
2204     if (bsp->counter >= CM_BULKMAX)
2205         return CM_ERROR_STOPNOW;
2206
2207     thyper.LowPart = cm_data.buf_blockSize;
2208     thyper.HighPart = 0;
2209     thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2210
2211     /* thyper is now the first byte past the end of the record we're
2212      * interested in, and bsp->bufOffset is the first byte of the record
2213      * we're interested in.
2214      * Skip data in the others.
2215      * Skip '.' and '..'
2216      */
2217     if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2218         return 0;
2219     if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2220         return CM_ERROR_STOPNOW;
2221     if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2222         return 0;
2223
2224     cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2225     tscp = cm_FindSCache(&tfid);
2226     if (tscp) {
2227         if (lock_TryWrite(&tscp->rw)) {
2228             /* we have an entry that we can look at */
2229             if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2230                 /* we have a callback on it.  Don't bother
2231                  * fetching this stat entry, since we're happy
2232                  * with the info we have.
2233                  */
2234                 lock_ReleaseWrite(&tscp->rw);
2235                 cm_ReleaseSCache(tscp);
2236                 return 0;
2237             }
2238             lock_ReleaseWrite(&tscp->rw);
2239         }       /* got lock */
2240         cm_ReleaseSCache(tscp);
2241     }   /* found entry */
2242
2243 #ifdef AFS_FREELANCE_CLIENT
2244     // yj: if this is a mountpoint under root.afs then we don't want it
2245     // to be bulkstat-ed, instead, we call getSCache directly and under
2246     // getSCache, it is handled specially.
2247     if  ( cm_freelanceEnabled &&
2248           tfid.cell==AFS_FAKE_ROOT_CELL_ID && 
2249           tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2250           !(tfid.vnode==0x1 && tfid.unique==0x1) )
2251     {       
2252         osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2253         return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2254     }
2255 #endif /* AFS_FREELANCE_CLIENT */
2256
2257     i = bsp->counter++;
2258     bsp->fids[i].Volume = scp->fid.volume;
2259     bsp->fids[i].Vnode = tfid.vnode;
2260     bsp->fids[i].Unique = tfid.unique;
2261     return 0;
2262 }       
2263
2264 afs_int32
2265 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2266 {
2267     afs_int32 code = 0;
2268     AFSCBFids fidStruct;
2269     AFSBulkStats statStruct;
2270     cm_conn_t *connp;
2271     AFSCBs callbackStruct;
2272     long filex;
2273     AFSVolSync volSync;
2274     cm_callbackRequest_t cbReq;
2275     long filesThisCall;
2276     long i;
2277     long j;
2278     cm_scache_t *scp;
2279     cm_fid_t tfid;
2280     struct rx_connection * rxconnp;
2281     int inlinebulk = 0;         /* Did we use InlineBulkStatus RPC or not? */
2282         
2283     /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2284      * make the calls to create the entries.  Handle AFSCBMAX files at a
2285      * time.
2286      */
2287     for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2288         filesThisCall = bbp->counter - filex;
2289         if (filesThisCall > AFSCBMAX) 
2290             filesThisCall = AFSCBMAX;
2291
2292         fidStruct.AFSCBFids_len = filesThisCall;
2293         fidStruct.AFSCBFids_val = &bbp->fids[filex];
2294         statStruct.AFSBulkStats_len = filesThisCall;
2295         statStruct.AFSBulkStats_val = &bbp->stats[filex];
2296         callbackStruct.AFSCBs_len = filesThisCall;
2297         callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2298         cm_StartCallbackGrantingCall(NULL, &cbReq);
2299         osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2300         do {
2301             code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2302             if (code) 
2303                 continue;
2304
2305             rxconnp = cm_GetRxConn(connp);
2306             if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2307                 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2308                                      &statStruct, &callbackStruct, &volSync);
2309                 if (code == RXGEN_OPCODE) {
2310                     cm_SetServerNoInlineBulk(connp->serverp, 0);
2311                 } else {
2312                     inlinebulk = 1;
2313                 }
2314             }
2315             if (!inlinebulk) {
2316                 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2317                                         &statStruct, &callbackStruct, &volSync);
2318             }
2319             rx_PutConnection(rxconnp);
2320
2321         } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2322                              &volSync, NULL, &cbReq, code));
2323         code = cm_MapRPCError(code, reqp);
2324         if (code)
2325             osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x", 
2326                       inlinebulk ? "Inline" : "", code);
2327         else
2328             osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2329
2330         /* may as well quit on an error, since we're not going to do
2331          * much better on the next immediate call, either.
2332          */
2333         if (code) {
2334             cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2335             break;
2336         }
2337
2338         /* otherwise, we should do the merges */
2339         for (i = 0; i<filesThisCall; i++) {
2340             j = filex + i;
2341             cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2342             code = cm_GetSCache(&tfid, &scp, userp, reqp);
2343             if (code != 0) 
2344                 continue;
2345
2346             /* otherwise, if this entry has no callback info, 
2347              * merge in this.
2348              */
2349             lock_ObtainWrite(&scp->rw);
2350             /* now, we have to be extra paranoid on merging in this
2351              * information, since we didn't use cm_SyncOp before
2352              * starting the fetch to make sure that no bad races
2353              * were occurring.  Specifically, we need to make sure
2354              * we don't obliterate any newer information in the
2355              * vnode than have here.
2356              *
2357              * Right now, be pretty conservative: if there's a
2358              * callback or a pending call, skip it.
2359              */
2360             if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2361                  && !(scp->flags &
2362                        (CM_SCACHEFLAG_FETCHING
2363                          | CM_SCACHEFLAG_STORING
2364                          | CM_SCACHEFLAG_SIZESTORING))) {
2365                 cm_EndCallbackGrantingCall(scp, &cbReq,
2366                                             &bbp->callbacks[j],
2367                                             CM_CALLBACK_MAINTAINCOUNT);
2368                 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, 0);
2369             }       
2370             lock_ReleaseWrite(&scp->rw);
2371             cm_ReleaseSCache(scp);
2372         } /* all files in the response */
2373         /* now tell it to drop the count,
2374          * after doing the vnode processing above */
2375         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2376     }   /* while there are still more files to process */
2377
2378     /* If we did the InlineBulk RPC pull out the return code and log it */
2379     if (inlinebulk) {
2380         if ((&bbp->stats[0])->errorCode) {
2381             osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d", 
2382                      (&bbp->stats[0])->errorCode);
2383             code = (&bbp->stats[0])->errorCode;
2384         }
2385     }
2386
2387     return code;
2388 }
2389
2390 /* called with a write locked scp and a pointer to a buffer.  Make bulk stat
2391  * calls on all undeleted files in the page of the directory specified.
2392  */
2393 afs_int32
2394 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2395                cm_req_t *reqp)
2396 {
2397     afs_int32 code;
2398     cm_bulkStat_t *bbp;
2399
2400     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2401
2402     /* should be on a buffer boundary */
2403     osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2404
2405     bbp = malloc(sizeof(cm_bulkStat_t));
2406     memset(bbp, 0, sizeof(cm_bulkStat_t));
2407     bbp->bufOffset = *offsetp;
2408
2409     lock_ReleaseWrite(&dscp->rw);
2410     /* first, assemble the file IDs we need to stat */
2411     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2412
2413     /* if we failed, bail out early */
2414     if (code && code != CM_ERROR_STOPNOW) {
2415         free(bbp);
2416         lock_ObtainWrite(&dscp->rw);
2417         return code;
2418     }
2419
2420     code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2421     osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2422
2423     lock_ObtainWrite(&dscp->rw);
2424     free(bbp);
2425     return 0;
2426 }       
2427
2428 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2429 {
2430     long mask;
2431
2432     /* initialize store back mask as inexpensive local variable */
2433     mask = 0;
2434     memset(statusp, 0, sizeof(AFSStoreStatus));
2435
2436     /* copy out queued info from scache first, if scp passed in */
2437     if (scp) {
2438         if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2439             statusp->ClientModTime = scp->clientModTime;
2440             mask |= AFS_SETMODTIME;
2441             scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2442         }
2443     }
2444
2445     if (attrp) {
2446         /* now add in our locally generated request */
2447         if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2448             statusp->ClientModTime = attrp->clientModTime;
2449             mask |= AFS_SETMODTIME;
2450         }
2451         if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2452             statusp->UnixModeBits = attrp->unixModeBits;
2453             mask |= AFS_SETMODE;
2454         }
2455         if (attrp->mask & CM_ATTRMASK_OWNER) {
2456             statusp->Owner = attrp->owner;
2457             mask |= AFS_SETOWNER;
2458         }
2459         if (attrp->mask & CM_ATTRMASK_GROUP) {
2460             statusp->Group = attrp->group;
2461             mask |= AFS_SETGROUP;
2462         }
2463     }
2464     statusp->Mask = mask;
2465 }       
2466
2467 /* set the file size, and make sure that all relevant buffers have been
2468  * truncated.  Ensure that any partially truncated buffers have been zeroed
2469  * to the end of the buffer.
2470  */
2471 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2472                    cm_req_t *reqp)
2473 {
2474     long code;
2475     int shrinking;
2476
2477     /* start by locking out buffer creation */
2478     lock_ObtainWrite(&scp->bufCreateLock);
2479
2480     /* verify that this is a file, not a dir or a symlink */
2481     lock_ObtainWrite(&scp->rw);
2482     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2483                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2484     if (code) 
2485         goto done;
2486     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2487
2488     if (scp->fileType != CM_SCACHETYPE_FILE) {
2489         code = CM_ERROR_ISDIR;
2490         goto done;
2491     }
2492
2493   startover:
2494     if (LargeIntegerLessThan(*sizep, scp->length))
2495         shrinking = 1;
2496     else
2497         shrinking = 0;
2498
2499     lock_ReleaseWrite(&scp->rw);
2500
2501     /* can't hold scp->rw lock here, since we may wait for a storeback to
2502      * finish if the buffer package is cleaning a buffer by storing it to
2503      * the server.
2504      */
2505     if (shrinking)
2506         buf_Truncate(scp, userp, reqp, sizep);
2507
2508     /* now ensure that file length is short enough, and update truncPos */
2509     lock_ObtainWrite(&scp->rw);
2510
2511     /* make sure we have a callback (so we have the right value for the
2512      * length), and wait for it to be safe to do a truncate.
2513      */
2514     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2515                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2516                       | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2517
2518     /* If we only have 'i' bits, then we should still be able to set
2519        the size of a file we created. */
2520     if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2521         code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2522                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2523                          | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2524     }
2525
2526     if (code) 
2527         goto done;
2528
2529     if (LargeIntegerLessThan(*sizep, scp->length)) {
2530         /* a real truncation.  If truncPos is not set yet, or is bigger
2531          * than where we're truncating the file, set truncPos to this
2532          * new value.
2533          */
2534         if (!shrinking)
2535             goto startover;
2536         if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2537              || LargeIntegerLessThan(*sizep, scp->length)) {
2538             /* set trunc pos */
2539             scp->truncPos = *sizep;
2540             scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2541         }
2542         /* in either case, the new file size has been changed */
2543         scp->length = *sizep;
2544         scp->mask |= CM_SCACHEMASK_LENGTH;
2545     }
2546     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2547         /* really extending the file */
2548         scp->length = *sizep;
2549         scp->mask |= CM_SCACHEMASK_LENGTH;
2550     }
2551
2552     /* done successfully */
2553     code = 0;
2554
2555     cm_SyncOpDone(scp, NULL, 
2556                    CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2557                    | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2558
2559   done:
2560     lock_ReleaseWrite(&scp->rw);
2561     lock_ReleaseWrite(&scp->bufCreateLock);
2562
2563     return code;
2564 }
2565
2566 /* set the file size or other attributes (but not both at once) */
2567 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2568                 cm_req_t *reqp)
2569 {
2570     long code;
2571     AFSFetchStatus afsOutStatus;
2572     AFSVolSync volSync;
2573     cm_conn_t *connp;
2574     AFSFid tfid;
2575     AFSStoreStatus afsInStatus;
2576     struct rx_connection * rxconnp;
2577
2578     /* handle file length setting */
2579     if (attrp->mask & CM_ATTRMASK_LENGTH)
2580         return cm_SetLength(scp, &attrp->length, userp, reqp);
2581
2582     lock_ObtainWrite(&scp->rw);
2583     /* otherwise, we have to make an RPC to get the status */
2584     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2585     if (code) {
2586         lock_ReleaseWrite(&scp->rw);
2587         return code;
2588     }
2589     lock_ConvertWToR(&scp->rw);
2590
2591     /* make the attr structure */
2592     cm_StatusFromAttr(&afsInStatus, scp, attrp);
2593
2594     tfid.Volume = scp->fid.volume;
2595     tfid.Vnode = scp->fid.vnode;
2596     tfid.Unique = scp->fid.unique;
2597     lock_ReleaseRead(&scp->rw);
2598
2599     /* now make the RPC */
2600     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2601     do {
2602         code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2603         if (code) 
2604             continue;
2605
2606         rxconnp = cm_GetRxConn(connp);
2607         code = RXAFS_StoreStatus(rxconnp, &tfid,
2608                                   &afsInStatus, &afsOutStatus, &volSync);
2609         rx_PutConnection(rxconnp);
2610
2611     } while (cm_Analyze(connp, userp, reqp,
2612                          &scp->fid, &volSync, NULL, NULL, code));
2613     code = cm_MapRPCError(code, reqp);
2614
2615     if (code)
2616         osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2617     else
2618         osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2619
2620     lock_ObtainWrite(&scp->rw);
2621     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2622     if (code == 0)
2623         cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
2624                         CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2625         
2626     /* if we're changing the mode bits, discard the ACL cache, 
2627      * since we changed the mode bits.
2628      */
2629     if (afsInStatus.Mask & AFS_SETMODE) 
2630         cm_FreeAllACLEnts(scp);
2631     lock_ReleaseWrite(&scp->rw);
2632     return code;
2633 }       
2634
2635 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2636                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2637 {       
2638     cm_conn_t *connp;
2639     long code;
2640     AFSFid dirAFSFid;
2641     cm_callbackRequest_t cbReq;
2642     AFSFid newAFSFid;
2643     cm_fid_t newFid;
2644     cm_scache_t *scp = NULL;
2645     int didEnd;
2646     AFSStoreStatus inStatus;
2647     AFSFetchStatus updatedDirStatus;
2648     AFSFetchStatus newFileStatus;
2649     AFSCallBack newFileCallback;
2650     AFSVolSync volSync;
2651     struct rx_connection * rxconnp;
2652     cm_dirOp_t dirop;
2653     fschar_t * fnamep = NULL;
2654
2655     /* can't create names with @sys in them; must expand it manually first.
2656      * return "invalid request" if they try.
2657      */
2658     if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2659         return CM_ERROR_ATSYS;
2660     }
2661
2662 #ifdef AFS_FREELANCE_CLIENT
2663     /* Freelance root volume does not hold files */
2664     if (cm_freelanceEnabled &&
2665         dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2666         dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2667     {
2668         return CM_ERROR_NOACCESS;
2669     }
2670 #endif /* AFS_FREELANCE_CLIENT */
2671
2672     /* before starting the RPC, mark that we're changing the file data, so
2673      * that someone who does a chmod will know to wait until our call
2674      * completes.
2675      */
2676     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2677     lock_ObtainWrite(&dscp->rw);
2678     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2679     lock_ReleaseWrite(&dscp->rw);
2680     if (code == 0) {
2681         cm_StartCallbackGrantingCall(NULL, &cbReq);
2682     } else {
2683         cm_EndDirOp(&dirop);
2684     }
2685     if (code) {
2686         return code;
2687     }
2688     didEnd = 0;
2689
2690     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2691
2692     cm_StatusFromAttr(&inStatus, NULL, attrp);
2693
2694     /* try the RPC now */
2695     osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2696     do {
2697         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2698         if (code) 
2699             continue;
2700
2701         dirAFSFid.Volume = dscp->fid.volume;
2702         dirAFSFid.Vnode = dscp->fid.vnode;
2703         dirAFSFid.Unique = dscp->fid.unique;
2704
2705         rxconnp = cm_GetRxConn(connp);
2706         code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2707                                  &inStatus, &newAFSFid, &newFileStatus,
2708                                  &updatedDirStatus, &newFileCallback,
2709                                  &volSync);
2710         rx_PutConnection(rxconnp);
2711
2712     } while (cm_Analyze(connp, userp, reqp,
2713                          &dscp->fid, &volSync, NULL, &cbReq, code));
2714     code = cm_MapRPCError(code, reqp);
2715         
2716     if (code)
2717         osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2718     else
2719         osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2720
2721     if (dirop.scp) {
2722         lock_ObtainWrite(&dirop.scp->dirlock);
2723         dirop.lockType = CM_DIRLOCK_WRITE;
2724     }
2725     lock_ObtainWrite(&dscp->rw);
2726     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2727     if (code == 0) {
2728         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2729     }
2730     lock_ReleaseWrite(&dscp->rw);
2731
2732     /* now try to create the file's entry, too, but be careful to 
2733      * make sure that we don't merge in old info.  Since we weren't locking
2734      * out any requests during the file's creation, we may have pretty old
2735      * info.
2736      */
2737     if (code == 0) {
2738         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2739         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2740         if (code == 0) {
2741             lock_ObtainWrite(&scp->rw);
2742             scp->creator = userp;               /* remember who created it */
2743             if (!cm_HaveCallback(scp)) {
2744                 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2745                                userp, 0);
2746                 cm_EndCallbackGrantingCall(scp, &cbReq,
2747                                            &newFileCallback, 0);
2748                 didEnd = 1;     
2749             }       
2750             lock_ReleaseWrite(&scp->rw);
2751         }
2752     }
2753
2754     /* make sure we end things properly */
2755     if (!didEnd)
2756         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2757
2758     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2759         cm_DirCreateEntry(&dirop, fnamep, &newFid);
2760 #ifdef USE_BPLUS
2761         cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2762 #endif
2763     }
2764     cm_EndDirOp(&dirop);
2765
2766     if (fnamep)
2767         free(fnamep);
2768
2769     if (scp) {
2770         if (scpp)
2771             *scpp = scp;
2772         else
2773             cm_ReleaseSCache(scp);
2774     }
2775     return code;
2776 }       
2777
2778 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2779 {
2780     long code;
2781
2782     code = buf_CleanVnode(scp, userp, reqp);
2783     if (code == 0) {
2784         lock_ObtainWrite(&scp->rw);
2785
2786         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2787                           | CM_SCACHEMASK_CLIENTMODTIME
2788                           | CM_SCACHEMASK_LENGTH))
2789             code = cm_StoreMini(scp, userp, reqp);
2790
2791         if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2792             code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2793             scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2794         }
2795
2796         lock_ReleaseWrite(&scp->rw);
2797     }
2798     return code;
2799 }
2800
2801 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2802                 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2803 {
2804     cm_conn_t *connp;
2805     long code;
2806     AFSFid dirAFSFid;
2807     cm_callbackRequest_t cbReq;
2808     AFSFid newAFSFid;
2809     cm_fid_t newFid;
2810     cm_scache_t *scp = NULL;
2811     int didEnd;
2812     AFSStoreStatus inStatus;
2813     AFSFetchStatus updatedDirStatus;
2814     AFSFetchStatus newDirStatus;
2815     AFSCallBack newDirCallback;
2816     AFSVolSync volSync;
2817     struct rx_connection * rxconnp;
2818     cm_dirOp_t dirop;
2819     fschar_t * fnamep = NULL;
2820
2821     /* can't create names with @sys in them; must expand it manually first.
2822      * return "invalid request" if they try.
2823      */
2824     if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2825         return CM_ERROR_ATSYS;
2826     }
2827
2828 #ifdef AFS_FREELANCE_CLIENT
2829     /* Freelance root volume does not hold subdirectories */
2830     if (cm_freelanceEnabled &&
2831         dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2832         dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2833     {
2834         return CM_ERROR_NOACCESS;
2835     }
2836 #endif /* AFS_FREELANCE_CLIENT */
2837
2838     /* before starting the RPC, mark that we're changing the directory
2839      * data, so that someone who does a chmod on the dir will wait until
2840      * our call completes.
2841      */
2842     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2843     lock_ObtainWrite(&dscp->rw);
2844     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2845     lock_ReleaseWrite(&dscp->rw);
2846     if (code == 0) {
2847         cm_StartCallbackGrantingCall(NULL, &cbReq);
2848     } else {
2849         cm_EndDirOp(&dirop);
2850     }
2851     if (code) {
2852         return code;
2853     }
2854     didEnd = 0;
2855
2856     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2857     cm_StatusFromAttr(&inStatus, NULL, attrp);
2858
2859     /* try the RPC now */
2860     osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2861     do {
2862         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2863         if (code) 
2864             continue;
2865
2866         dirAFSFid.Volume = dscp->fid.volume;
2867         dirAFSFid.Vnode = dscp->fid.vnode;
2868         dirAFSFid.Unique = dscp->fid.unique;
2869
2870         rxconnp = cm_GetRxConn(connp);
2871         code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
2872                               &inStatus, &newAFSFid, &newDirStatus,
2873                               &updatedDirStatus, &newDirCallback,
2874                               &volSync);
2875         rx_PutConnection(rxconnp);
2876
2877     } while (cm_Analyze(connp, userp, reqp,
2878                         &dscp->fid, &volSync, NULL, &cbReq, code));
2879     code = cm_MapRPCError(code, reqp);
2880         
2881     if (code)
2882         osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2883     else
2884         osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2885
2886     if (dirop.scp) {
2887         lock_ObtainWrite(&dirop.scp->dirlock);
2888         dirop.lockType = CM_DIRLOCK_WRITE;
2889     }
2890     lock_ObtainWrite(&dscp->rw);
2891     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2892     if (code == 0) {
2893         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2894     }
2895     lock_ReleaseWrite(&dscp->rw);
2896
2897     /* now try to create the new dir's entry, too, but be careful to 
2898      * make sure that we don't merge in old info.  Since we weren't locking
2899      * out any requests during the file's creation, we may have pretty old
2900      * info.
2901      */
2902     if (code == 0) {
2903         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2904         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2905         if (code == 0) {
2906             lock_ObtainWrite(&scp->rw);
2907             if (!cm_HaveCallback(scp)) {
2908                 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2909                                 userp, 0);
2910                 cm_EndCallbackGrantingCall(scp, &cbReq,
2911                                             &newDirCallback, 0);
2912                 didEnd = 1;             
2913             }
2914             lock_ReleaseWrite(&scp->rw);
2915         }
2916     }
2917
2918     /* make sure we end things properly */
2919     if (!didEnd)
2920         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2921
2922     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2923         cm_DirCreateEntry(&dirop, fnamep, &newFid);
2924 #ifdef USE_BPLUS
2925         cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2926 #endif
2927     }
2928     cm_EndDirOp(&dirop);
2929
2930     free(fnamep);
2931
2932     if (scp) {
2933         if (scpp)
2934             *scpp = scp;
2935         else
2936             cm_ReleaseSCache(scp);
2937     }
2938
2939     /* and return error code */
2940     return code;
2941 }       
2942
2943 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
2944              cm_user_t *userp, cm_req_t *reqp)
2945 {
2946     cm_conn_t *connp;
2947     long code = 0;
2948     AFSFid dirAFSFid;
2949     AFSFid existingAFSFid;
2950     AFSFetchStatus updatedDirStatus;
2951     AFSFetchStatus newLinkStatus;
2952     AFSVolSync volSync;
2953     struct rx_connection * rxconnp;
2954     cm_dirOp_t dirop;
2955     fschar_t * fnamep = NULL;
2956
2957     if (dscp->fid.cell != sscp->fid.cell ||
2958         dscp->fid.volume != sscp->fid.volume) {
2959         return CM_ERROR_CROSSDEVLINK;
2960     }
2961
2962     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2963     lock_ObtainWrite(&dscp->rw);
2964     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2965     lock_ReleaseWrite(&dscp->rw);
2966     if (code != 0)
2967         cm_EndDirOp(&dirop);
2968
2969     if (code)
2970         return code;
2971
2972     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2973
2974     /* try the RPC now */
2975     osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2976     do {
2977         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2978         if (code) continue;
2979
2980         dirAFSFid.Volume = dscp->fid.volume;
2981         dirAFSFid.Vnode = dscp->fid.vnode;
2982         dirAFSFid.Unique = dscp->fid.unique;
2983
2984         existingAFSFid.Volume = sscp->fid.volume;
2985         existingAFSFid.Vnode = sscp->fid.vnode;
2986         existingAFSFid.Unique = sscp->fid.unique;
2987
2988         rxconnp = cm_GetRxConn(connp);
2989         code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
2990             &newLinkStatus, &updatedDirStatus, &volSync);
2991         rx_PutConnection(rxconnp);
2992         osi_Log1(afsd_logp,"  RXAFS_Link returns 0x%x", code);
2993
2994     } while (cm_Analyze(connp, userp, reqp,
2995         &dscp->fid, &volSync, NULL, NULL, code));
2996
2997     code = cm_MapRPCError(code, reqp);
2998
2999     if (code)
3000         osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3001     else
3002         osi_Log0(afsd_logp, "CALL Link SUCCESS");
3003
3004     if (dirop.scp) {
3005         lock_ObtainWrite(&dirop.scp->dirlock);
3006         dirop.lockType = CM_DIRLOCK_WRITE;
3007     }
3008     lock_ObtainWrite(&dscp->rw);
3009     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3010     if (code == 0) {
3011         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3012     }
3013     lock_ReleaseWrite(&dscp->rw);
3014
3015     if (code == 0) {
3016         if (cm_CheckDirOpForSingleChange(&dirop)) {
3017             cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3018 #ifdef USE_BPLUS
3019             cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3020 #endif
3021         }
3022     }
3023     cm_EndDirOp(&dirop);
3024
3025     free(fnamep);
3026
3027     return code;
3028 }
3029
3030 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3031                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3032 {
3033     cm_conn_t *connp;
3034     long code;
3035     AFSFid dirAFSFid;
3036     AFSFid newAFSFid;
3037     cm_fid_t newFid;
3038     cm_scache_t *scp;
3039     AFSStoreStatus inStatus;
3040     AFSFetchStatus updatedDirStatus;
3041     AFSFetchStatus newLinkStatus;
3042     AFSVolSync volSync;
3043     struct rx_connection * rxconnp;
3044     cm_dirOp_t dirop;
3045     fschar_t *fnamep = NULL;
3046
3047     /* before starting the RPC, mark that we're changing the directory data,
3048      * so that someone who does a chmod on the dir will wait until our
3049      * call completes.
3050      */
3051     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3052     lock_ObtainWrite(&dscp->rw);
3053     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3054     lock_ReleaseWrite(&dscp->rw);
3055     if (code != 0)
3056         cm_EndDirOp(&dirop);
3057     if (code) {
3058         return code;
3059     }
3060
3061     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3062
3063     cm_StatusFromAttr(&inStatus, NULL, attrp);
3064
3065     /* try the RPC now */
3066     osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3067     do {
3068         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3069         if (code) 
3070             continue;
3071
3072         dirAFSFid.Volume = dscp->fid.volume;
3073         dirAFSFid.Vnode = dscp->fid.vnode;
3074         dirAFSFid.Unique = dscp->fid.unique;
3075
3076         rxconnp = cm_GetRxConn(connp);
3077         code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3078                               &inStatus, &newAFSFid, &newLinkStatus,
3079                               &updatedDirStatus, &volSync);
3080         rx_PutConnection(rxconnp);
3081
3082     } while (cm_Analyze(connp, userp, reqp,
3083                          &dscp->fid, &volSync, NULL, NULL, code));
3084     code = cm_MapRPCError(code, reqp);
3085         
3086     if (code)
3087         osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3088     else
3089         osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3090
3091     if (dirop.scp) {
3092         lock_ObtainWrite(&dirop.scp->dirlock);
3093         dirop.lockType = CM_DIRLOCK_WRITE;
3094     }
3095     lock_ObtainWrite(&dscp->rw);
3096     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3097     if (code == 0) {
3098         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3099     }
3100     lock_ReleaseWrite(&dscp->rw);
3101
3102     if (code == 0) {
3103         if (cm_CheckDirOpForSingleChange(&dirop)) {
3104             cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3105
3106             cm_DirCreateEntry(&dirop, fnamep, &newFid);
3107 #ifdef USE_BPLUS
3108             cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3109 #endif
3110         }
3111     }
3112     cm_EndDirOp(&dirop);
3113
3114     /* now try to create the new dir's entry, too, but be careful to 
3115      * make sure that we don't merge in old info.  Since we weren't locking
3116      * out any requests during the file's creation, we may have pretty old
3117      * info.
3118      */
3119     if (code == 0) {
3120         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3121         code = cm_GetSCache(&newFid, &scp, userp, reqp);
3122         if (code == 0) {
3123             lock_ObtainWrite(&scp->rw);
3124             if (!cm_HaveCallback(scp)) {
3125                 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3126                                 userp, 0);
3127             }       
3128             lock_ReleaseWrite(&scp->rw);
3129             cm_ReleaseSCache(scp);
3130         }
3131     }
3132
3133     free(fnamep);
3134         
3135     /* and return error code */
3136     return code;
3137 }
3138
3139 /*! \brief Remove a directory
3140
3141   Encapsulates a call to RXAFS_RemoveDir().
3142
3143   \param[in] dscp cm_scache_t for the directory containing the
3144       directory to be removed.
3145
3146   \param[in] fnamep This will be the original name of the directory
3147       as known to the file server.   It will be passed in to RXAFS_RemoveDir().
3148       This parameter is optional.  If it is not provided the value
3149       will be looked up.
3150
3151   \param[in] cnamep Normalized name used to update the local
3152       directory caches.
3153
3154   \param[in] userp cm_user_t for the request.
3155
3156   \param[in] reqp Request tracker.
3157 */
3158 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3159 {
3160     cm_conn_t *connp;
3161     long code;
3162     AFSFid dirAFSFid;
3163     int didEnd;
3164     AFSFetchStatus updatedDirStatus;
3165     AFSVolSync volSync;
3166     struct rx_connection * rxconnp;
3167     cm_dirOp_t dirop;
3168     cm_scache_t *scp = NULL;
3169     int free_fnamep = FALSE;
3170
3171     if (fnamep == NULL) {
3172         code = -1;
3173 #ifdef USE_BPLUS
3174         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
3175         if (code == 0) {
3176             code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3177             if (code == 0)
3178                 free_fnamep = TRUE;
3179             cm_EndDirOp(&dirop);
3180         }
3181 #endif
3182         if (code)
3183             goto done;
3184     }
3185
3186     code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3187     if (code)
3188         goto done;
3189
3190     /* before starting the RPC, mark that we're changing the directory data,
3191      * so that someone who does a chmod on the dir will wait until our
3192      * call completes.
3193      */
3194     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3195     lock_ObtainWrite(&dscp->rw);
3196     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3197     lock_ReleaseWrite(&dscp->rw);
3198     if (code) {
3199         cm_EndDirOp(&dirop);
3200         goto done;
3201     }
3202     didEnd = 0;
3203
3204     /* try the RPC now */
3205     osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3206     do {
3207         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3208         if (code) 
3209             continue;
3210
3211         dirAFSFid.Volume = dscp->fid.volume;
3212         dirAFSFid.Vnode = dscp->fid.vnode;
3213         dirAFSFid.Unique = dscp->fid.unique;
3214
3215         rxconnp = cm_GetRxConn(connp);
3216         code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3217                                &updatedDirStatus, &volSync);
3218         rx_PutConnection(rxconnp);
3219
3220     } while (cm_Analyze(connp, userp, reqp,
3221                         &dscp->fid, &volSync, NULL, NULL, code));
3222     code = cm_MapRPCErrorRmdir(code, reqp);
3223
3224     if (code)
3225         osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3226     else
3227         osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3228
3229     if (dirop.scp) {
3230         lock_ObtainWrite(&dirop.scp->dirlock);
3231         dirop.lockType = CM_DIRLOCK_WRITE;
3232     }
3233     lock_ObtainWrite(&dscp->rw);
3234     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3235     if (code == 0) {
3236         cm_dnlcRemove(dscp, cnamep); 
3237         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3238     }
3239     lock_ReleaseWrite(&dscp->rw);
3240
3241     if (code == 0) {
3242         if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3243             cm_DirDeleteEntry(&dirop, fnamep);
3244 #ifdef USE_BPLUS
3245             cm_BPlusDirDeleteEntry(&dirop, cnamep);
3246 #endif
3247         }
3248     }
3249     cm_EndDirOp(&dirop);
3250
3251     if (scp) {
3252         cm_ReleaseSCache(scp);
3253         if (code == 0) {
3254             lock_ObtainWrite(&scp->rw);
3255             scp->flags |= CM_SCACHEFLAG_DELETED;
3256             lock_ReleaseWrite(&scp->rw);
3257         }
3258     }
3259
3260   done:
3261     if (free_fnamep)
3262         free(fnamep);
3263
3264     /* and return error code */
3265     return code;
3266 }
3267
3268 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3269 {
3270     /* grab mutex on contents */
3271     lock_ObtainWrite(&scp->rw);
3272
3273     /* reset the prefetch info */
3274     scp->prefetch.base.LowPart = 0;             /* base */
3275     scp->prefetch.base.HighPart = 0;
3276     scp->prefetch.end.LowPart = 0;              /* and end */
3277     scp->prefetch.end.HighPart = 0;
3278
3279     /* release mutex on contents */
3280     lock_ReleaseWrite(&scp->rw);
3281
3282     /* we're done */
3283     return 0;
3284 }       
3285
3286 /*! \brief Rename a file or directory
3287
3288   Encapsulates a RXAFS_Rename() call.
3289
3290   \param[in] oldDscp cm_scache_t for the directory containing the old
3291       name.
3292
3293   \param[in] oldNamep The original old name known to the file server.
3294       This is the name that will be passed into the RXAFS_Rename().
3295       If it is not provided, it will be looked up.
3296
3297   \param[in] normalizedOldNamep Normalized old name.  This is used for
3298   updating local directory caches.
3299
3300   \param[in] newDscp cm_scache_t for the directory containing the new
3301   name.
3302
3303   \param[in] newNamep New name. Normalized.
3304
3305   \param[in] userp cm_user_t for the request.
3306
3307   \param[in,out] reqp Request tracker.
3308
3309 */
3310 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3311                cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3312                cm_req_t *reqp)
3313 {
3314     cm_conn_t *connp;
3315     long code;
3316     AFSFid oldDirAFSFid;
3317     AFSFid newDirAFSFid;
3318     int didEnd;
3319     AFSFetchStatus updatedOldDirStatus;
3320     AFSFetchStatus updatedNewDirStatus;
3321     AFSVolSync volSync;
3322     int oneDir;
3323     struct rx_connection * rxconnp;
3324     cm_dirOp_t oldDirOp;
3325     cm_fid_t   fileFid;
3326     int        diropCode = -1;
3327     cm_dirOp_t newDirOp;
3328     fschar_t * newNamep = NULL;
3329     int free_oldNamep = FALSE;
3330     cm_scache_t *oldScp = NULL, *newScp = NULL;
3331
3332     if (cOldNamep == NULL || cNewNamep == NULL ||
3333         cm_ClientStrLen(cOldNamep) == 0 ||
3334         cm_ClientStrLen(cNewNamep) == 0)
3335         return CM_ERROR_INVAL;
3336
3337     /* 
3338      * Before we permit the operation, make sure that we do not already have
3339      * an object in the destination directory that has a case-insensitive match
3340      * for this name UNLESS the matching object is the object we are renaming.
3341      */
3342     code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3343     if (code) {
3344         osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed", 
3345                  oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3346         goto done;
3347     }
3348
3349     code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3350     if (code == 0) {
3351         /* found a matching object with the new name */
3352         if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3353             /* and they don't match so return an error */
3354             osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists", 
3355                       newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3356             code = CM_ERROR_EXISTS;
3357         }
3358         cm_ReleaseSCache(newScp);
3359         newScp = NULL;
3360     } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3361         code = CM_ERROR_EXISTS;
3362     } else {
3363         code = 0;
3364     }
3365     if (code) 
3366         goto done;
3367
3368     if (oldNamep == NULL) {
3369         code = -1;
3370 #ifdef USE_BPLUS
3371         code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ, &oldDirOp);
3372         if (code == 0) {
3373             code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3374             if (code == 0)
3375                 free_oldNamep = TRUE;
3376             cm_EndDirOp(&oldDirOp);
3377         }
3378 #endif
3379         if (code) {
3380             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed", 
3381                       oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3382             goto done;
3383         }
3384     }
3385
3386
3387     /* before starting the RPC, mark that we're changing the directory data,
3388      * so that someone who does a chmod on the dir will wait until our call
3389      * completes.  We do this in vnode order so that we don't deadlock,
3390      * which makes the code a little verbose.
3391      */
3392     if (oldDscp == newDscp) {
3393         /* check for identical names */
3394         if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3395             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL", 
3396                       oldDscp, newDscp);
3397             code = CM_ERROR_RENAME_IDENTICAL;
3398             goto done;
3399         }
3400
3401         oneDir = 1;
3402         cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3403         lock_ObtainWrite(&oldDscp->rw);
3404         cm_dnlcRemove(oldDscp, cOldNamep);
3405         cm_dnlcRemove(oldDscp, cNewNamep);
3406         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3407                           CM_SCACHESYNC_STOREDATA);
3408         lock_ReleaseWrite(&oldDscp->rw);
3409         if (code != 0) {
3410             cm_EndDirOp(&oldDirOp);
3411         }
3412     }
3413     else {
3414         /* two distinct dir vnodes */
3415         oneDir = 0;
3416         if (oldDscp->fid.cell != newDscp->fid.cell ||
3417              oldDscp->fid.volume != newDscp->fid.volume) {
3418             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK", 
3419                       oldDscp, newDscp);
3420             code = CM_ERROR_CROSSDEVLINK;
3421             goto done;
3422         }
3423
3424         /* shouldn't happen that we have distinct vnodes for two
3425          * different files, but could due to deliberate attack, or
3426          * stale info.  Avoid deadlocks and quit now.
3427          */
3428         if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3429             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision", 
3430                       oldDscp, newDscp);
3431             code = CM_ERROR_CROSSDEVLINK;
3432             goto done;
3433         }
3434
3435         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3436             cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3437             lock_ObtainWrite(&oldDscp->rw);
3438             cm_dnlcRemove(oldDscp, cOldNamep);
3439             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3440                              CM_SCACHESYNC_STOREDATA);
3441             lock_ReleaseWrite(&oldDscp->rw);
3442             if (code != 0)
3443                 cm_EndDirOp(&oldDirOp);
3444             if (code == 0) {
3445                 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3446                 lock_ObtainWrite(&newDscp->rw);
3447                 cm_dnlcRemove(newDscp, cNewNamep);
3448                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3449                                  CM_SCACHESYNC_STOREDATA);
3450                 lock_ReleaseWrite(&newDscp->rw);
3451                 if (code) {
3452                     cm_EndDirOp(&newDirOp);
3453
3454                     /* cleanup first one */
3455                     lock_ObtainWrite(&oldDscp->rw);
3456                     cm_SyncOpDone(oldDscp, NULL,
3457                                    CM_SCACHESYNC_STOREDATA);
3458                     lock_ReleaseWrite(&oldDscp->rw);
3459                     cm_EndDirOp(&oldDirOp);
3460                 }       
3461             }
3462         }
3463         else {
3464             /* lock the new vnode entry first */
3465             cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3466             lock_ObtainWrite(&newDscp->rw);
3467             cm_dnlcRemove(newDscp, cNewNamep);
3468             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3469                               CM_SCACHESYNC_STOREDATA);
3470             lock_ReleaseWrite(&newDscp->rw);
3471             if (code != 0)
3472                 cm_EndDirOp(&newDirOp);
3473             if (code == 0) {
3474                 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3475                 lock_ObtainWrite(&oldDscp->rw);
3476                 cm_dnlcRemove(oldDscp, cOldNamep);
3477                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3478                                   CM_SCACHESYNC_STOREDATA);
3479                 lock_ReleaseWrite(&oldDscp->rw);
3480                 if (code != 0)
3481                     cm_EndDirOp(&oldDirOp);
3482                 if (code) {
3483                     /* cleanup first one */
3484                     lock_ObtainWrite(&newDscp->rw);
3485                     cm_SyncOpDone(newDscp, NULL,
3486                                    CM_SCACHESYNC_STOREDATA);
3487                     lock_ReleaseWrite(&newDscp->rw);
3488                     cm_EndDirOp(&newDirOp);
3489                 }       
3490             }
3491         }
3492     }   /* two distinct vnodes */
3493
3494     if (code) 
3495         goto done;
3496
3497     didEnd = 0;
3498
3499     newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3500
3501     /* try the RPC now */
3502     osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p", 
3503               oldDscp, newDscp);
3504     do {
3505         code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3506         if (code) 
3507             continue;
3508
3509         oldDirAFSFid.Volume = oldDscp->fid.volume;
3510         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3511         oldDirAFSFid.Unique = oldDscp->fid.unique;
3512         newDirAFSFid.Volume = newDscp->fid.volume;
3513         newDirAFSFid.Vnode = newDscp->fid.vnode;
3514         newDirAFSFid.Unique = newDscp->fid.unique;
3515
3516         rxconnp = cm_GetRxConn(connp);
3517         code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3518                             &newDirAFSFid, newNamep,
3519                             &updatedOldDirStatus, &updatedNewDirStatus,
3520                             &volSync);
3521         rx_PutConnection(rxconnp);
3522
3523     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3524                          &volSync, NULL, NULL, code));
3525     code = cm_MapRPCError(code, reqp);
3526         
3527     if (code)
3528         osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3529     else
3530         osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3531
3532     /* update the individual stat cache entries for the directories */
3533     if (oldDirOp.scp) {
3534         lock_ObtainWrite(&oldDirOp.scp->dirlock);
3535         oldDirOp.lockType = CM_DIRLOCK_WRITE;
3536     }
3537     lock_ObtainWrite(&oldDscp->rw);
3538     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3539
3540     if (code == 0)
3541         cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3542                        userp, CM_MERGEFLAG_DIROP);
3543     lock_ReleaseWrite(&oldDscp->rw);
3544
3545     if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3546 #ifdef USE_BPLUS
3547         diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3548         if (diropCode == CM_ERROR_INEXACT_MATCH)
3549             diropCode = 0;
3550         else if (diropCode == EINVAL)
3551 #endif
3552             diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3553
3554         if (diropCode == 0) {
3555             if (oneDir) {
3556                 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3557 #ifdef USE_BPLUS        
3558                 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3559 #endif
3560             }
3561                 
3562             if (diropCode == 0) { 
3563                 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3564 #ifdef USE_BPLUS
3565                 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3566 #endif  
3567             }
3568         }
3569     }
3570     cm_EndDirOp(&oldDirOp);
3571
3572     /* and update it for the new one, too, if necessary */
3573     if (!oneDir) {
3574         if (newDirOp.scp) {
3575             lock_ObtainWrite(&newDirOp.scp->dirlock);
3576             newDirOp.lockType = CM_DIRLOCK_WRITE;
3577         }
3578         lock_ObtainWrite(&newDscp->rw);
3579         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3580         if (code == 0)
3581             cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3582                             userp, CM_MERGEFLAG_DIROP);
3583         lock_ReleaseWrite(&newDscp->rw);
3584
3585 #if 0 
3586         /* 
3587          * The following optimization does not work.  
3588          * When the file server processed a RXAFS_Rename() request the 
3589          * FID of the object being moved between directories is not 
3590          * preserved.  The client does not know the new FID nor the 
3591          * version number of the target.  Not only can we not create
3592          * the directory entry in the new directory, but we can't 
3593          * preserve the cached data for the file.  It must be re-read
3594          * from the file server.  - jaltman, 2009/02/20
3595          */
3596         if (code == 0) {
3597             /* we only make the local change if we successfully made
3598                the change in the old directory AND there was only one
3599                change in the new directory */
3600             if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3601                 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3602 #ifdef USE_BPLUS
3603                 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3604 #endif
3605             }
3606         }
3607 #endif /* 0 */
3608         cm_EndDirOp(&newDirOp);
3609     }
3610
3611     /* 
3612      * After the rename the file server has invalidated the callbacks
3613      * on the file that was moved nor do we have a directory reference 
3614      * to it anymore.
3615      */
3616     lock_ObtainWrite(&oldScp->rw);
3617     cm_DiscardSCache(oldScp);
3618     lock_ReleaseWrite(&oldScp->rw);
3619
3620   done:
3621     if (oldScp)
3622         cm_ReleaseSCache(oldScp);
3623
3624     if (free_oldNamep)
3625         free(oldNamep);
3626
3627     free(newNamep);
3628
3629     /* and return error code */
3630     return code;
3631 }
3632
3633 /* Byte range locks:
3634
3635    The OpenAFS Windows client has to fake byte range locks given no
3636    server side support for such locks.  This is implemented as keyed
3637    byte range locks on the cache manager.
3638
3639    Keyed byte range locks:
3640
3641    Each cm_scache_t structure keeps track of a list of keyed locks.
3642    The key for a lock identifies an owner of a set of locks (referred
3643    to as a client).  Each key is represented by a value.  The set of
3644    key values used within a specific cm_scache_t structure form a
3645    namespace that has a scope of just that cm_scache_t structure.  The
3646    same key value can be used with another cm_scache_t structure and
3647    correspond to a completely different client.  However it is
3648    advantageous for the SMB or IFS layer to make sure that there is a
3649    1-1 mapping between client and keys over all cm_scache_t objects.
3650
3651    Assume a client C has key Key(C) (although, since the scope of the
3652    key is a cm_scache_t, the key can be Key(C,S), where S is the
3653    cm_scache_t.  But assume a 1-1 relation between keys and clients).
3654    A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3655    inclusive (a.k.a. [O,O+L-1]).  The function Key(x) is implemented
3656    through cm_generateKey() function for both SMB and IFS.
3657
3658    The list of locks for a cm_scache_t object S is maintained in
3659    S->fileLocks.  The cache manager will set a lock on the AFS file
3660    server in order to assert the locks in S->fileLocks.  If only
3661    shared locks are in place for S, then the cache manager will obtain
3662    a LockRead lock, while if there are any exclusive locks, it will
3663    obtain a LockWrite lock.  If the exclusive locks are all released
3664    while the shared locks remain, then the cache manager will
3665    downgrade the lock from LockWrite to LockRead.  Similarly, if an
3666    exclusive lock is obtained when only shared locks exist, then the
3667    cache manager will try to upgrade the lock from LockRead to
3668    LockWrite.
3669
3670    Each lock L owned by client C maintains a key L->key such that
3671    L->key == Key(C), the effective range defined by L->LOffset and
3672    L->LLength such that the range of bytes affected by the lock is
3673    (L->LOffset, +L->LLength), a type maintained in L->LockType which
3674    is either exclusive or shared.
3675
3676    Lock states:
3677
3678    A lock exists iff it is in S->fileLocks for some cm_scache_t
3679    S. Existing locks are in one of the following states: ACTIVE,
3680    WAITLOCK, WAITUNLOCK, LOST, DELETED.
3681
3682    The following sections describe each lock and the associated
3683    transitions.
3684
3685    1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3686       the lock with the AFS file server.  This type of lock can be
3687       exercised by a client to read or write to the locked region (as
3688       the lock allows).
3689
3690       1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3691         server lock that was required to assert the lock.  Before
3692         marking the lock as lost, the cache manager checks if the file
3693         has changed on the server.  If the file has not changed, then
3694         the cache manager will attempt to obtain a new server lock
3695         that is sufficient to assert the client side locks for the
3696         file.  If any of these fail, the lock is marked as LOST.
3697         Otherwise, it is left as ACTIVE.
3698
3699       1.2 ACTIVE->DELETED: Lock is released.
3700
3701    2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3702       grants the lock but the lock is yet to be asserted with the AFS
3703       file server.  Once the file server grants the lock, the state
3704       will transition to an ACTIVE lock.
3705
3706       2.1 WAITLOCK->ACTIVE: The server granted the lock.
3707
3708       2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3709         waiting.
3710
3711       2.3 WAITLOCK->LOST: One or more locks from this client were
3712         marked as LOST.  No further locks will be granted to this
3713         client until all lost locks are removed.
3714
3715    3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3716       receives a request for a lock that conflicts with an existing
3717       ACTIVE or WAITLOCK lock.  The lock will be placed in the queue
3718       and will be granted at such time the conflicting locks are
3719       removed, at which point the state will transition to either
3720       WAITLOCK or ACTIVE.
3721
3722       3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed.  The
3723         current serverLock is sufficient to assert this lock, or a
3724         sufficient serverLock is obtained.
3725
3726       3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3727         however the required serverLock is yet to be asserted with the
3728         server.
3729
3730       3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3731         released.
3732
3733       3.5 WAITUNLOCK->LOST: One or more locks from this client were
3734         marked as LOST.  No further locks will be granted to this
3735         client until all lost locks are removed.
3736
3737    4. LOST: A lock L is LOST if the server lock that was required to
3738       assert the lock could not be obtained or if it could not be
3739       extended, or if other locks by the same client were LOST.
3740       Essentially, once a lock is LOST, the contract between the cache
3741       manager and that specific client is no longer valid.
3742
3743       The cache manager rechecks the server lock once every minute and
3744       extends it as appropriate.  If this is not done for 5 minutes,
3745       the AFS file server will release the lock (the 5 minute timeout
3746       is based on current file server code and is fairly arbitrary).
3747       Once released, the lock cannot be re-obtained without verifying
3748       that the contents of the file hasn't been modified since the
3749       time the lock was released.  Re-obtaining the lock without
3750       verifying this may lead to data corruption.  If the lock can not
3751       be obtained safely, then all active locks for the cm_scache_t
3752       are marked as LOST.
3753
3754       4.1 LOST->DELETED: The lock is released.
3755
3756    5. DELETED: The lock is no longer relevant.  Eventually, it will
3757       get removed from the cm_scache_t. In the meantime, it will be
3758       treated as if it does not exist.
3759
3760       5.1 DELETED->not exist: The lock is removed from the
3761         cm_scache_t.
3762
3763    The following are classifications of locks based on their state.
3764
3765    6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK.  These locks
3766       have been accepted by the cache manager, but may or may not have
3767       been granted back to the client.
3768
3769    7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3770
3771    8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3772
3773    Lock operation:
3774
3775    A client C can READ range (Offset,+Length) of a file represented by
3776    cm_scache_t S iff (1):
3777
3778    1. for all _a_ in (Offset,+Length), all of the following is true:
3779
3780        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3781          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3782          shared.
3783
3784        1.2 For each LOST lock L in S->fileLocks such that _a_ in
3785          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3786          Key(C)
3787
3788        (When locks are lost on an cm_scache_t, all locks are lost.  By
3789        4.2 (below), if there is an exclusive LOST lock, then there
3790        can't be any overlapping ACTIVE locks.)
3791
3792    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3793
3794    2. for all _a_ in (Offset,+Length), one of the following is true:
3795
3796        2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3797          does not exist a LOST lock L such that _a_ in
3798          (L->LOffset,+L->LLength).
3799
3800        2.2 Byte _a_ of S is owned by C under lock L (as specified in
3801          1.2) AND L->LockType is exclusive.
3802
3803    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3804
3805    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3806       true:
3807
3808        3.1 If L->LockType is exclusive then there does NOT exist a
3809          ACCEPTED lock M in S->fileLocks such that _a_ in
3810          (M->LOffset,+M->LLength).
3811
3812          (If we count all QUEUED locks then we hit cases such as
3813          cascading waiting locks where the locks later on in the queue
3814          can be granted without compromising file integrity.  On the
3815          other hand if only ACCEPTED locks are considered, then locks
3816          that were received earlier may end up waiting for locks that
3817          were received later to be unlocked. The choice of ACCEPTED
3818          locks was made to mimic the Windows byte range lock
3819          semantics.)
3820
3821        3.2 If L->LockType is shared then for each ACCEPTED lock M in
3822          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3823          M->LockType is shared.
3824
3825    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3826
3827        4.1 M->key != Key(C)
3828
3829        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3830          and (M->LOffset,+M->LLength) do not intersect.
3831
3832          (Note: If a client loses a lock, it loses all locks.
3833          Subsequently, it will not be allowed to obtain any more locks
3834          until all existing LOST locks that belong to the client are
3835          released.  Once all locks are released by a single client,
3836          there exists no further contract between the client and AFS
3837          about the contents of the file, hence the client can then
3838          proceed to obtain new locks and establish a new contract.
3839
3840          This doesn't quite work as you think it should, because most
3841          applications aren't built to deal with losing locks they
3842          thought they once had.  For now, we don't have a good
3843          solution to lost locks.
3844
3845          Also, for consistency reasons, we have to hold off on
3846          granting locks that overlap exclusive LOST locks.)
3847
3848    A client C can only unlock locks L in S->fileLocks which have
3849    L->key == Key(C).
3850
3851    The representation and invariants are as follows:
3852
3853    - Each cm_scache_t structure keeps:
3854
3855        - A queue of byte-range locks (cm_scache_t::fileLocks) which
3856          are of type cm_file_lock_t.
3857
3858        - A record of the highest server-side lock that has been
3859          obtained for this object (cm_scache_t::serverLock), which is
3860          one of (-1), LockRead, LockWrite.
3861
3862        - A count of ACCEPTED exclusive and shared locks that are in the
3863          queue (cm_scache_t::sharedLocks and
3864          cm_scache_t::exclusiveLocks)
3865
3866    - Each cm_file_lock_t structure keeps:
3867
3868        - The type of lock (cm_file_lock_t::LockType)
3869
3870        - The key associated with the lock (cm_file_lock_t::key)
3871
3872        - The offset and length of the lock (cm_file_lock_t::LOffset
3873          and cm_file_lock_t::LLength)
3874
3875        - The state of the lock.
3876
3877        - Time of issuance or last successful extension
3878
3879    Semantic invariants:
3880
3881        I1. The number of ACCEPTED locks in S->fileLocks are
3882            (S->sharedLocks + S->exclusiveLocks)
3883
3884    External invariants:
3885
3886        I3. S->serverLock is the lock that we have asserted with the
3887            AFS file server for this cm_scache_t.
3888
3889        I4. S->serverLock == LockRead iff there is at least one ACTIVE
3890            shared lock, but no ACTIVE exclusive locks.
3891
3892        I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3893            exclusive lock.
3894
3895        I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3896            M->key == L->key IMPLIES M is LOST or DELETED.
3897
3898    --asanka
3899  */
3900
3901 #define IS_LOCK_ACTIVE(lockp)     (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3902
3903 #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)
3904
3905 #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)
3906
3907 #define IS_LOCK_LOST(lockp)       (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3908
3909 #define IS_LOCK_DELETED(lockp)    (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3910
3911 /* unsafe */
3912 #define IS_LOCK_ACCEPTED(lockp)   (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3913
3914 /* unsafe */
3915 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3916
3917 /* unsafe */
3918 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3919
3920 /* unsafe */
3921 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3922
3923 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3924 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3925 #else
3926 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3927 #endif
3928
3929 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3930
3931 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3932 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3933 #else
3934 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3935
3936 /* This should really be defined in any build that this code is being
3937    compiled. */
3938 #error  VICED_CAPABILITY_WRITELOCKACL not defined.
3939 #endif
3940
3941 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3942 {
3943     afs_int64 int_begin;
3944     afs_int64 int_end;
3945
3946     int_begin = MAX(pos->offset, neg->offset);
3947     int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3948
3949     if (int_begin < int_end) {
3950         if (int_begin == pos->offset) {
3951             pos->length = pos->offset + pos->length - int_end;
3952             pos->offset = int_end;
3953         } else if (int_end == pos->offset + pos->length) {
3954             pos->length = int_begin - pos->offset;
3955         }
3956
3957         /* We only subtract ranges if the resulting range is
3958            contiguous.  If we try to support non-contigous ranges, we
3959            aren't actually improving performance. */
3960     }
3961 }
3962
3963 /* Called with scp->rw held.  Returns 0 if all is clear to read the
3964    specified range by the client identified by key.
3965  */
3966 long cm_LockCheckRead(cm_scache_t *scp, 
3967                       LARGE_INTEGER LOffset, 
3968                       LARGE_INTEGER LLength, 
3969                       cm_key_t key)
3970 {
3971 #ifndef ADVISORY_LOCKS
3972
3973     cm_file_lock_t *fileLock;
3974     osi_queue_t *q;
3975     long code = 0;
3976     cm_range_t range;
3977     int substract_ranges = FALSE;
3978
3979     range.offset = LOffset.QuadPart;
3980     range.length = LLength.QuadPart;
3981
3982     /*
3983
3984      1. for all _a_ in (Offset,+Length), all of the following is true:
3985
3986        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3987          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3988          shared.
3989
3990        1.2 For each LOST lock L in S->fileLocks such that _a_ in
3991          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3992          Key(C)
3993
3994     */
3995
3996     lock_ObtainRead(&cm_scacheLock);
3997
3998     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3999         fileLock = 
4000             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4001
4002         if (INTERSECT_RANGE(range, fileLock->range)) {
4003             if (IS_LOCK_ACTIVE(fileLock)) {
4004                 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4005
4006                     /* If there is an active lock for this client, it
4007                        is safe to substract ranges.*/
4008                     cm_LockRangeSubtract(&range, &fileLock->range);
4009                     substract_ranges = TRUE;
4010                 } else {
4011                     if (fileLock->lockType != LockRead) {
4012                         code = CM_ERROR_LOCK_CONFLICT;
4013                         break;
4014                     }
4015
4016                     /* even if the entire range is locked for reading,
4017                        we still can't grant the lock at this point
4018                        because the client may have lost locks. That
4019                        is, unless we have already seen an active lock
4020                        belonging to the client, in which case there
4021                        can't be any lost locks for this client. */
4022                     if (substract_ranges)
4023                         cm_LockRangeSubtract(&range, &fileLock->range);
4024                 }
4025             } else if (IS_LOCK_LOST(fileLock) &&
4026                        (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4027                 code = CM_ERROR_BADFD;
4028                 break;
4029             }
4030         }
4031     }
4032
4033     lock_ReleaseRead(&cm_scacheLock);
4034
4035     osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4036               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4037
4038     return code;
4039
4040 #else
4041
4042     return 0;
4043
4044 #endif
4045 }
4046
4047 /* Called with scp->rw held.  Returns 0 if all is clear to write the
4048    specified range by the client identified by key.
4049  */
4050 long cm_LockCheckWrite(cm_scache_t *scp,
4051                        LARGE_INTEGER LOffset,
4052                        LARGE_INTEGER LLength,
4053                        cm_key_t key)
4054 {
4055 #ifndef ADVISORY_LOCKS
4056
4057     cm_file_lock_t *fileLock;
4058     osi_queue_t *q;
4059     long code = 0;
4060     cm_range_t range;
4061
4062     range.offset = LOffset.QuadPart;
4063     range.length = LLength.QuadPart;
4064
4065     /*
4066    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4067
4068    2. for all _a_ in (Offset,+Length), one of the following is true:
4069
4070        2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4071          lock L such that _a_ in (L->LOffset,+L->LLength).
4072
4073        2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4074          exclusive.
4075     */
4076
4077     lock_ObtainRead(&cm_scacheLock);
4078
4079     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4080         fileLock = 
4081             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4082
4083         if (INTERSECT_RANGE(range, fileLock->range)) {
4084             if (IS_LOCK_ACTIVE(fileLock)) {
4085                 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4086                     if (fileLock->lockType == LockWrite) {
4087
4088                         /* if there is an active lock for this client, it
4089                            is safe to substract ranges */
4090                         cm_LockRangeSubtract(&range, &fileLock->range);
4091                     } else {
4092                         code = CM_ERROR_LOCK_CONFLICT;
4093                         break;
4094                     }
4095                 } else {
4096                     code = CM_ERROR_LOCK_CONFLICT;
4097                     break;
4098                 }
4099             } else if (IS_LOCK_LOST(fileLock)) {
4100                 code = CM_ERROR_BADFD;
4101                 break;
4102             }
4103         }
4104     }
4105
4106     lock_ReleaseRead(&cm_scacheLock);
4107
4108     osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4109               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4110
4111     return code;
4112
4113 #else
4114
4115     return 0;
4116
4117 #endif
4118 }
4119
4120 /* Called with cm_scacheLock write locked */
4121 static cm_file_lock_t * cm_GetFileLock(void) {
4122     cm_file_lock_t * l;
4123
4124     l = (cm_file_lock_t *) cm_freeFileLocks;
4125     if (l) {
4126         osi_QRemove(&cm_freeFileLocks, &l->q);
4127     } else {
4128         l = malloc(sizeof(cm_file_lock_t));
4129         osi_assertx(l, "null cm_file_lock_t");
4130     }
4131
4132     memset(l, 0, sizeof(cm_file_lock_t));
4133
4134     return l;
4135 }
4136
4137 /* Called with cm_scacheLock write locked */
4138 static void cm_PutFileLock(cm_file_lock_t *l) {
4139     osi_QAdd(&cm_freeFileLocks, &l->q);
4140 }
4141
4142 /* called with scp->rw held.  May release it during processing, but
4143    leaves it held on exit. */
4144 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4145                    cm_req_t * reqp) {
4146     long code = 0;
4147     AFSFid tfid;
4148     cm_fid_t cfid;
4149     cm_conn_t * connp;
4150     struct rx_connection * rxconnp;
4151     AFSVolSync volSync;
4152     afs_uint32 reqflags = reqp->flags;
4153
4154     tfid.Volume = scp->fid.volume;
4155     tfid.Vnode = scp->fid.vnode;
4156     tfid.Unique = scp->fid.unique;
4157     cfid = scp->fid;
4158
4159     osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4160
4161     reqp->flags |= CM_REQ_NORETRY;
4162     lock_ReleaseWrite(&scp->rw);
4163
4164     do {
4165         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4166         if (code) 
4167             break;
4168
4169         rxconnp = cm_GetRxConn(connp);
4170         code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4171                              &volSync);
4172         rx_PutConnection(rxconnp);
4173
4174     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4175                         NULL, NULL, code));
4176
4177     code = cm_MapRPCError(code, reqp);
4178     if (code) {
4179         osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4180     } else {
4181         osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4182     }
4183
4184     lock_ObtainWrite(&scp->rw);
4185     reqp->flags = reqflags;
4186     return code;
4187 }
4188
4189 /* called with scp->rw held.  Releases it during processing */
4190 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4191                        cm_req_t * reqp) {
4192     long code = 0;
4193     AFSFid tfid;
4194     cm_fid_t cfid;
4195     cm_conn_t * connp;
4196     struct rx_connection * rxconnp;
4197     AFSVolSync volSync;
4198
4199     tfid.Volume = scp->fid.volume;
4200     tfid.Vnode = scp->fid.vnode;
4201     tfid.Unique = scp->fid.unique;
4202     cfid = scp->fid;
4203
4204     lock_ReleaseWrite(&scp->rw);
4205
4206     osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4207
4208     do {
4209         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4210         if (code) 
4211             break;
4212
4213         rxconnp = cm_GetRxConn(connp);
4214         code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4215         rx_PutConnection(rxconnp);
4216
4217     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4218                         NULL, NULL, code));
4219     code = cm_MapRPCError(code, reqp);
4220     if (code)
4221         osi_Log1(afsd_logp,
4222                  "CALL ReleaseLock FAILURE, code 0x%x", code);
4223     else
4224         osi_Log0(afsd_logp,
4225                  "CALL ReleaseLock SUCCESS");
4226         
4227     lock_ObtainWrite(&scp->rw);
4228
4229     return code;
4230 }
4231
4232 /* called with scp->rw held.  May release it during processing, but
4233    will exit with lock held.
4234
4235    This will return:
4236
4237    - 0 if the user has permission to get the specified lock for the scp
4238
4239    - CM_ERROR_NOACCESS if not
4240
4241    Any other error from cm_SyncOp will be sent down untranslated.
4242
4243    If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4244    phas_insert (if non-NULL) will receive a boolean value indicating
4245    whether the user has INSERT permission or not.
4246 */
4247 long cm_LockCheckPerms(cm_scache_t * scp,
4248                        int lock_type,
4249                        cm_user_t * userp,
4250                        cm_req_t * reqp,
4251                        int * phas_insert)
4252 {
4253     long rights = 0;
4254     long code = 0, code2 = 0;
4255
4256     /* lock permissions are slightly tricky because of the 'i' bit.
4257        If the user has PRSFS_LOCK, she can read-lock the file.  If the
4258        user has PRSFS_WRITE, she can write-lock the file.  However, if
4259        the user has PRSFS_INSERT, then she can write-lock new files,
4260        but not old ones.  Since we don't have information about
4261        whether a file is new or not, we assume that if the user owns
4262        the scp, then she has the permissions that are granted by
4263        PRSFS_INSERT. */
4264
4265     osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4266              scp, lock_type, userp);
4267
4268     if (lock_type == LockRead)
4269         rights |= PRSFS_LOCK;
4270     else if (lock_type == LockWrite)
4271         rights |= PRSFS_WRITE | PRSFS_LOCK;
4272     else {
4273         /* hmmkay */
4274         osi_assertx(FALSE, "invalid lock type");
4275         return 0;
4276     }
4277
4278     if (phas_insert)
4279         *phas_insert = FALSE;
4280
4281     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4282                      CM_SCACHESYNC_GETSTATUS |
4283                      CM_SCACHESYNC_NEEDCALLBACK);
4284
4285     if (phas_insert && scp->creator == userp) {
4286
4287         /* If this file was created by the user, then we check for
4288            PRSFS_INSERT.  If the file server is recent enough, then
4289            this should be sufficient for her to get a write-lock (but
4290            not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4291            indicates whether a file server supports getting write
4292            locks when the user only has PRSFS_INSERT. 
4293            
4294            If the file was not created by the user we skip the check
4295            because the INSERT bit will not apply to this user even
4296            if it is set.
4297          */
4298
4299         code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4300                          CM_SCACHESYNC_GETSTATUS |
4301                          CM_SCACHESYNC_NEEDCALLBACK);
4302
4303         if (code2 == CM_ERROR_NOACCESS) {
4304             osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4305         } else {
4306             *phas_insert = TRUE;
4307             osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4308         }
4309     }
4310
4311     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4312
4313     osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4314
4315     return code;
4316 }
4317
4318 /* called with scp->rw held */
4319 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4320              LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4321              cm_key_t key,
4322              int allowWait, cm_user_t *userp, cm_req_t *reqp,
4323              cm_file_lock_t **lockpp)
4324 {
4325     long code = 0;
4326     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4327     cm_file_lock_t *fileLock;
4328     osi_queue_t *q;
4329     cm_range_t range;
4330     int wait_unlock = FALSE;
4331     int force_client_lock = FALSE;
4332
4333     osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4334              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4335     osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait, 
4336              key.process_id, key.session_id, key.file_id);
4337
4338     /*
4339    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4340
4341    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4342       true:
4343
4344        3.1 If L->LockType is exclusive then there does NOT exist a
4345          ACCEPTED lock M in S->fileLocks such that _a_ in
4346          (M->LOffset,+M->LLength).
4347
4348        3.2 If L->LockType is shared then for each ACCEPTED lock M in
4349          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4350          M->LockType is shared.
4351
4352    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4353
4354        4.1 M->key != Key(C)
4355
4356        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4357          and (M->LOffset,+M->LLength) do not intersect.
4358     */
4359
4360     range.offset = LOffset.QuadPart;
4361     range.length = LLength.QuadPart;
4362
4363     lock_ObtainRead(&cm_scacheLock);
4364
4365     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4366         fileLock =
4367             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4368
4369         if (IS_LOCK_LOST(fileLock)) {
4370             if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4371                 code = CM_ERROR_BADFD;
4372                 break;
4373             } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4374                 code = CM_ERROR_WOULDBLOCK;
4375                 wait_unlock = TRUE;
4376                 break;
4377             }
4378         }
4379
4380         /* we don't need to check for deleted locks here since deleted
4381            locks are dequeued from scp->fileLocks */
4382         if (IS_LOCK_ACCEPTED(fileLock) &&
4383            INTERSECT_RANGE(range, fileLock->range)) {
4384
4385             if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4386                 fileLock->lockType != LockRead) {
4387                 wait_unlock = TRUE;
4388                 code = CM_ERROR_WOULDBLOCK;
4389                 break;
4390             }
4391         }
4392     }
4393
4394     lock_ReleaseRead(&cm_scacheLock);
4395
4396     if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4397         if (Which == scp->serverLock ||
4398            (Which == LockRead && scp->serverLock == LockWrite)) {
4399
4400             int has_insert = 0;
4401
4402             /* we already have the lock we need */
4403             osi_Log3(afsd_logp, "   we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]", 
4404                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4405
4406             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4407
4408             /* special case: if we don't have permission to read-lock
4409                the file, then we force a clientside lock.  This is to
4410                compensate for applications that obtain a read-lock for
4411                reading files off of directories that don't grant
4412                read-locks to the user. */
4413             if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4414
4415                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4416                     osi_Log0(afsd_logp, "   User has no read-lock perms, but has INSERT perms.");
4417                     code = 0;
4418                 } else {
4419                     osi_Log0(afsd_logp, "   User has no read-lock perms. Forcing client-side lock");
4420                     force_client_lock = TRUE;
4421                 }
4422             }
4423
4424         } else if ((scp->exclusiveLocks > 0) ||
4425                    (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4426             int has_insert = 0;
4427
4428             /* We are already waiting for some other lock.  We should
4429                wait for the daemon to catch up instead of generating a
4430                flood of SetLock calls. */
4431             osi_Log3(afsd_logp, "   already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4432                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4433
4434             /* see if we have permission to create the lock in the
4435                first place. */
4436             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4437             if (code == 0)
4438                 code = CM_ERROR_WOULDBLOCK;
4439             else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4440
4441                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4442                     osi_Log0(afsd_logp,
4443                              "   User has no read-lock perms, but has INSERT perms.");
4444                     code = CM_ERROR_WOULDBLOCK;
4445                 } else {
4446                     osi_Log0(afsd_logp,
4447                              "   User has no read-lock perms. Forcing client-side lock");
4448                     force_client_lock = TRUE;
4449                 }
4450             }
4451
4452             /* leave any other codes as-is */
4453
4454         } else {
4455             int newLock;
4456             int check_data_version = FALSE;
4457             int has_insert = 0;
4458
4459             /* first check if we have permission to elevate or obtain
4460                the lock. */
4461             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4462             if (code) {
4463                 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4464                     (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4465                     osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
4466                     force_client_lock = TRUE;
4467                 }
4468                 goto check_code;
4469             }
4470
4471             /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4472
4473             if (scp->serverLock == LockRead && Which == LockWrite) {
4474
4475                 /* We want to escalate the lock to a LockWrite.
4476                  * Unfortunately that's not really possible without
4477                  * letting go of the current lock.  But for now we do
4478                  * it anyway. */
4479
4480                 osi_Log0(afsd_logp,
4481                          "   attempting to UPGRADE from LockRead to LockWrite.");
4482                 osi_Log1(afsd_logp,
4483                          "   dataVersion on scp: %I64d", scp->dataVersion);
4484
4485                 /* we assume at this point (because scp->serverLock
4486                    was valid) that we had a valid server lock. */
4487                 scp->lockDataVersion = scp->dataVersion;
4488                 check_data_version = TRUE;
4489         
4490                 code = cm_IntReleaseLock(scp, userp, reqp);
4491
4492                 if (code) {
4493                     /* We couldn't release the lock */
4494                     goto check_code;
4495                 } else {
4496                     scp->serverLock = -1;
4497                 }
4498             }
4499
4500             /* We need to obtain a server lock of type Which in order
4501              * to assert this file lock */
4502 #ifndef AGGRESSIVE_LOCKS
4503             newLock = Which;
4504 #else
4505             newLock = LockWrite;
4506 #endif
4507
4508             code = cm_IntSetLock(scp, userp, newLock, reqp);
4509
4510 #ifdef AGGRESSIVE_LOCKS
4511             if ((code == CM_ERROR_WOULDBLOCK ||
4512                  code == CM_ERROR_NOACCESS) && newLock != Which) {
4513                 /* we wanted LockRead.  We tried LockWrite. Now try
4514                  * LockRead again */
4515                 newLock = Which;
4516
4517                 /* am I sane? */
4518                 osi_assertx(newLock == LockRead, "lock type not read");
4519
4520                 code = cm_IntSetLock(scp, userp, newLock, reqp);
4521             }
4522 #endif
4523
4524             if (code == CM_ERROR_NOACCESS) {
4525                 if (Which == LockRead) {
4526                     if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4527                         long tcode;
4528                         /* We requested a read-lock, but we have permission to
4529                          * get a write-lock. Try that */
4530
4531                         tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4532
4533                         if (tcode == 0) {
4534                             newLock = LockWrite;
4535
4536                             osi_Log0(afsd_logp, "   User has 'i' perms and the request was for a LockRead.  Trying to get a LockWrite instead");
4537
4538                             code = cm_IntSetLock(scp, userp, newLock, reqp);
4539                         }
4540                     } else {
4541                         osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
4542                         force_client_lock = TRUE;
4543                     }
4544                 } else if (Which == LockWrite &&
4545                            scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4546                     long tcode;
4547
4548                     /* Special case: if the lock request was for a
4549                      * LockWrite and the user owns the file and we weren't
4550                      * allowed to obtain the serverlock, we either lost a
4551                      * race (the permissions changed from under us), or we
4552                      * have 'i' bits, but we aren't allowed to lock the
4553                      * file. */
4554
4555                     /* check if we lost a race... */
4556                     tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4557
4558                     if (tcode == 0) {
4559                         osi_Log0(afsd_logp, "   User has 'i' perms but can't obtain write locks. Using client-side locks.");
4560                         force_client_lock = TRUE;
4561                     }
4562                 }
4563             }
4564
4565             if (code == 0 && check_data_version &&
4566                scp->dataVersion != scp->lockDataVersion) {
4567                 /* We lost a race.  Although we successfully obtained
4568                  * a lock, someone modified the file in between.  The
4569                  * locks have all been technically lost. */
4570
4571                 osi_Log0(afsd_logp,
4572                          "  Data version mismatch while upgrading lock.");
4573                 osi_Log2(afsd_logp,
4574                          "  Data versions before=%I64d, after=%I64d",
4575                          scp->lockDataVersion,
4576                          scp->dataVersion);
4577                 osi_Log1(afsd_logp,
4578                          "  Releasing stale lock for scp 0x%x", scp);
4579
4580                 code = cm_IntReleaseLock(scp, userp, reqp);
4581
4582                 scp->serverLock = -1;
4583
4584                 code = CM_ERROR_INVAL;
4585             } else if (code == 0) {
4586                 scp->serverLock = newLock;
4587                 scp->lockDataVersion = scp->dataVersion;
4588             }
4589
4590             if (code != 0 &&
4591                 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4592                 scp->serverLock == -1) {
4593                 /* Oops. We lost the lock. */
4594                 cm_LockMarkSCacheLost(scp);
4595             }
4596         }
4597     } else if (code == 0) {     /* server locks not enabled */
4598         osi_Log0(afsd_logp,
4599                  "  Skipping server lock for scp");
4600     }
4601
4602  check_code:
4603
4604     if (code != 0 && !force_client_lock) {
4605         /* Special case error translations
4606
4607            Applications don't expect certain errors from a
4608            LockFile/UnlockFile call.  We need to translate some error
4609            code to codes that apps expect and handle. */
4610
4611         /* We shouldn't actually need to handle this case since we
4612            simulate locks for RO scps anyway. */
4613         if (code == CM_ERROR_READONLY) {
4614             osi_Log0(afsd_logp, "   Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4615             code = CM_ERROR_NOACCESS;
4616         }
4617     }
4618
4619     if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4620         force_client_lock) {
4621
4622         /* clear the error if we are forcing a client lock, so we
4623            don't get confused later. */
4624         if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4625             code = 0;
4626
4627         lock_ObtainWrite(&cm_scacheLock);
4628         fileLock = cm_GetFileLock();
4629         lock_ReleaseWrite(&cm_scacheLock);
4630 #ifdef DEBUG
4631         fileLock->fid = scp->fid;
4632 #endif
4633         fileLock->key = key;
4634         fileLock->lockType = Which;
4635         cm_HoldUser(userp);
4636         fileLock->userp = userp;
4637         fileLock->range = range;
4638         fileLock->flags = (code == 0 ? 0 : 
4639                            ((wait_unlock)?
4640                             CM_FILELOCK_FLAG_WAITUNLOCK :
4641                             CM_FILELOCK_FLAG_WAITLOCK));
4642
4643         if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4644             fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4645
4646         fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4647
4648         lock_ObtainWrite(&cm_scacheLock);
4649         osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4650         cm_HoldSCacheNoLock(scp);
4651         fileLock->scp = scp;
4652         osi_QAdd(&cm_allFileLocks, &fileLock->q);
4653         lock_ReleaseWrite(&cm_scacheLock);
4654
4655         if (code != 0) {
4656             *lockpp = fileLock;
4657         }
4658
4659         if (IS_LOCK_CLIENTONLY(fileLock)) {
4660             scp->clientLocks++;
4661         } else if (IS_LOCK_ACCEPTED(fileLock)) {
4662             if (Which == LockRead)
4663                 scp->sharedLocks++;
4664             else
4665                 scp->exclusiveLocks++;
4666         }
4667
4668         osi_Log3(afsd_logp,
4669                  "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4670                  fileLock, fileLock->flags, scp);
4671         osi_Log4(afsd_logp,
4672                  "   exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4673                  scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4674                  (int)(signed char) scp->serverLock);
4675     } else {
4676         osi_Log1(afsd_logp,
4677                  "cm_Lock Rejecting lock (code = 0x%x)", code);
4678     }
4679
4680     return code;
4681 }
4682
4683 /* Called with scp->rw held */
4684 long cm_UnlockByKey(cm_scache_t * scp,
4685                     cm_key_t key,
4686                     int flags,
4687                     cm_user_t * userp,
4688                      cm_req_t * reqp)
4689 {
4690     long code = 0;
4691     cm_file_lock_t *fileLock;
4692     osi_queue_t *q, *qn;
4693     int n_unlocks = 0;
4694
4695     osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
4696              scp, key.process_id, key.session_id, key.file_id);
4697     osi_Log1(afsd_logp, "    flags=0x%x", flags);
4698
4699     lock_ObtainWrite(&cm_scacheLock);
4700
4701     for (q = scp->fileLocksH; q; q = qn) {
4702         qn = osi_QNext(q);
4703
4704         fileLock = (cm_file_lock_t *)
4705             ((char *) q - offsetof(cm_file_lock_t, fileq));
4706
4707 #ifdef DEBUG
4708         osi_Log4(afsd_logp, "   Checking lock[0x%x] range[%d,+%d] type[%d]",
4709                  fileLock,
4710                  (unsigned long) fileLock->range.offset,
4711                  (unsigned long) fileLock->range.length,
4712                 fileLock->lockType);
4713         osi_Log4(afsd_logp, "     key<0x%x, 0x%x, 0x%x> flags[0x%x]",
4714                  fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
4715                  fileLock->flags);
4716
4717         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4718             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4719             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4720                      fileLock->fid.cell,
4721                      fileLock->fid.volume,
4722                      fileLock->fid.vnode,
4723                      fileLock->fid.unique);
4724             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4725                      fileLock->scp->fid.cell,
4726                      fileLock->scp->fid.volume,
4727                      fileLock->scp->fid.vnode,
4728                      fileLock->scp->fid.unique);
4729             osi_assertx(FALSE, "invalid fid value");
4730         }
4731 #endif
4732
4733         if (!IS_LOCK_DELETED(fileLock) &&
4734             cm_KeyEquals(&fileLock->key, &key, flags)) {
4735             osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4736                     fileLock->range.offset,
4737                     fileLock->range.length,
4738                     fileLock->lockType);
4739
4740             if (scp->fileLocksT == q)
4741                 scp->fileLocksT = osi_QPrev(q);
4742             osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4743
4744             if (IS_LOCK_CLIENTONLY(fileLock)) {
4745                 scp->clientLocks--;
4746             } else if (IS_LOCK_ACCEPTED(fileLock)) {
4747                 if (fileLock->lockType == LockRead)
4748                     scp->sharedLocks--;
4749                 else
4750                     scp->exclusiveLocks--;
4751             }
4752
4753             fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4754
4755             cm_ReleaseUser(fileLock->userp);
4756             cm_ReleaseSCacheNoLock(scp);
4757
4758             fileLock->userp = NULL;
4759             fileLock->scp = NULL;
4760
4761             n_unlocks++;
4762         }
4763     }
4764
4765     lock_ReleaseWrite(&cm_scacheLock);
4766
4767     if (n_unlocks == 0) {
4768         osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4769         osi_Log3(afsd_logp, "   Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4770                  scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4771         
4772         return 0;
4773     }
4774
4775     osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4776
4777     osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4778     osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4779     osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4780
4781     if (!SERVERLOCKS_ENABLED(scp)) {
4782         osi_Log0(afsd_logp, "  Skipping server lock for scp");
4783         goto done;
4784     }
4785
4786     /* Ideally we would go through the rest of the locks to determine
4787      * if one or more locks that were formerly in WAITUNLOCK can now
4788      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4789      * scp->sharedLocks accordingly.  However, the retrying of locks
4790      * in that manner is done cm_RetryLock() manually.
4791      */
4792
4793     if (scp->serverLock == LockWrite &&
4794         scp->exclusiveLocks == 0 &&
4795         scp->sharedLocks > 0) {
4796
4797         /* The serverLock should be downgraded to LockRead */
4798         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
4799
4800         /* since scp->serverLock looked sane, we are going to assume
4801            that we have a valid server lock. */
4802         scp->lockDataVersion = scp->dataVersion;
4803         osi_Log1(afsd_logp, "  dataVersion on scp = %I64d", scp->dataVersion);
4804
4805         code = cm_IntReleaseLock(scp, userp, reqp);
4806
4807         if (code) {
4808             /* so we couldn't release it.  Just let the lock be for now */
4809             code = 0;
4810             goto done;
4811         } else {
4812             scp->serverLock = -1;
4813         }
4814
4815         code = cm_IntSetLock(scp, userp, LockRead, reqp);
4816
4817         if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4818             scp->serverLock = LockRead;
4819         } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4820             /* We lost a race condition.  Although we have a valid
4821                lock on the file, the data has changed and essentially
4822                we have lost the lock we had during the transition. */
4823
4824             osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4825             osi_Log2(afsd_logp, "  Data versions before=%I64d, after=%I64d",
4826                      scp->lockDataVersion,
4827                      scp->dataVersion);
4828             
4829             code = cm_IntReleaseLock(scp, userp, reqp);
4830
4831             code = CM_ERROR_INVAL;
4832             scp->serverLock = -1;
4833         }
4834
4835         if (code != 0 &&
4836             (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4837                 (scp->serverLock == -1)) {
4838                 /* Oopsie */
4839                 cm_LockMarkSCacheLost(scp);
4840             }
4841
4842         /* failure here has no bearing on the return value of
4843            cm_Unlock() */
4844         code = 0;
4845
4846     } else if (scp->serverLock != (-1) &&
4847               scp->exclusiveLocks == 0 &&
4848               scp->sharedLocks == 0) {
4849         /* The serverLock should be released entirely */
4850
4851         code = cm_IntReleaseLock(scp, userp, reqp);
4852
4853         if (code == 0)
4854             scp->serverLock = (-1);
4855     }
4856
4857  done:
4858
4859     osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4860     osi_Log4(afsd_logp, "   Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4861              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4862              (int)(signed char) scp->serverLock);
4863
4864     return code;
4865 }
4866
4867 long cm_Unlock(cm_scache_t *scp, 
4868                unsigned char sLockType,
4869                LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4870                cm_key_t key, 
4871                afs_uint32 flags,
4872                cm_user_t *userp, 
4873                cm_req_t *reqp)
4874 {
4875     long code = 0;
4876     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4877     cm_file_lock_t *fileLock;
4878     osi_queue_t *q;
4879     int release_userp = FALSE;
4880     int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
4881     int lock_found  = 0;
4882     LARGE_INTEGER RangeEnd;
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_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
4887              key.process_id, key.session_id, key.file_id, flags);
4888
4889     if (!exact_match)
4890         RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
4891
4892   try_again:
4893     lock_ObtainRead(&cm_scacheLock);
4894
4895     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4896         fileLock = (cm_file_lock_t *)
4897             ((char *) q - offsetof(cm_file_lock_t, fileq));
4898
4899 #ifdef DEBUG
4900         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4901             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4902             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4903                      fileLock->fid.cell,
4904                      fileLock->fid.volume,
4905                      fileLock->fid.vnode,
4906                      fileLock->fid.unique);
4907             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4908                      fileLock->scp->fid.cell,
4909                      fileLock->scp->fid.volume,
4910                      fileLock->scp->fid.vnode,
4911                      fileLock->scp->fid.unique);
4912             osi_assertx(FALSE, "invalid fid value");
4913         }
4914 #endif
4915         if (exact_match) {
4916             if (!IS_LOCK_DELETED(fileLock) &&
4917                  cm_KeyEquals(&fileLock->key, &key, 0) &&
4918                  fileLock->range.offset == LOffset.QuadPart &&
4919                  fileLock->range.length == LLength.QuadPart) {
4920                 lock_found = 1;
4921                 break;
4922             }
4923         } else {
4924
4925             if (!IS_LOCK_DELETED(fileLock) &&
4926                  cm_KeyEquals(&fileLock->key, &key, 0) &&
4927                  fileLock->range.offset >= LOffset.QuadPart &&
4928                  fileLock->range.offset < RangeEnd.QuadPart &&
4929                  (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
4930                 lock_found = 1;
4931                 break;
4932             }
4933         }
4934     }
4935
4936     if (!q) {
4937         lock_ReleaseRead(&cm_scacheLock);
4938
4939         if (lock_found && !exact_match) {
4940             code = 0;
4941             goto done;
4942         } else {
4943             osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
4944         
4945             /* The lock didn't exist anyway. *shrug* */
4946             return CM_ERROR_RANGE_NOT_LOCKED;
4947         }
4948     }
4949
4950     /* discard lock record */
4951     lock_ConvertRToW(&cm_scacheLock);
4952     if (scp->fileLocksT == q)
4953         scp->fileLocksT = osi_QPrev(q);
4954     osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4955
4956     /*
4957      * Don't delete it here; let the daemon delete it, to simplify
4958      * the daemon's traversal of the list.
4959      */
4960
4961     if (IS_LOCK_CLIENTONLY(fileLock)) {
4962         scp->clientLocks--;
4963     } else if (IS_LOCK_ACCEPTED(fileLock)) {
4964         if (fileLock->lockType == LockRead)
4965             scp->sharedLocks--;
4966         else
4967             scp->exclusiveLocks--;
4968     }
4969
4970     fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4971     if (userp != NULL) {
4972         cm_ReleaseUser(fileLock->userp);
4973     } else {
4974         userp = fileLock->userp;
4975         release_userp = TRUE;
4976     }
4977     fileLock->userp = NULL;
4978     cm_ReleaseSCacheNoLock(scp);
4979     fileLock->scp = NULL;
4980     lock_ReleaseWrite(&cm_scacheLock);
4981
4982     if (!SERVERLOCKS_ENABLED(scp)) {
4983         osi_Log0(afsd_logp, "   Skipping server locks for scp");
4984         goto done;
4985     }
4986
4987     /* Ideally we would go through the rest of the locks to determine
4988      * if one or more locks that were formerly in WAITUNLOCK can now
4989      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4990      * scp->sharedLocks accordingly.  However, the retrying of locks
4991      * in that manner is done cm_RetryLock() manually.
4992      */
4993
4994     if (scp->serverLock == LockWrite &&
4995         scp->exclusiveLocks == 0 &&
4996         scp->sharedLocks > 0) {
4997
4998         /* The serverLock should be downgraded to LockRead */
4999         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
5000
5001         /* Since we already had a lock, we assume that there is a
5002            valid server lock. */
5003         scp->lockDataVersion = scp->dataVersion;
5004         osi_Log1(afsd_logp, "   dataVersion on scp is %I64d", scp->dataVersion);
5005
5006         /* before we downgrade, make sure that we have enough
5007            permissions to get the read lock. */
5008         code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5009         if (code != 0) {
5010
5011             osi_Log0(afsd_logp, "  SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5012
5013             code = 0;
5014             goto done;
5015         }
5016
5017         code = cm_IntReleaseLock(scp, userp, reqp);
5018
5019         if (code) {
5020             /* so we couldn't release it.  Just let the lock be for now */
5021             code = 0;
5022             goto done;
5023         } else {
5024             scp->serverLock = -1;
5025         }
5026
5027         code = cm_IntSetLock(scp, userp, LockRead, reqp);
5028
5029         if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5030             scp->serverLock = LockRead;
5031         } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5032             /* Lost a race.  We obtained a new lock, but that is
5033                meaningless since someone modified the file
5034                inbetween. */
5035
5036             osi_Log0(afsd_logp,
5037                      "Data version mismatch while downgrading lock");
5038             osi_Log2(afsd_logp,
5039                      "  Data versions before=%I64d, after=%I64d",
5040                      scp->lockDataVersion,
5041                      scp->dataVersion);
5042             
5043             code = cm_IntReleaseLock(scp, userp, reqp);
5044
5045             scp->serverLock = -1;
5046             code = CM_ERROR_INVAL;
5047         }
5048
5049         if (code != 0 &&
5050             (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5051                 (scp->serverLock == -1)) {
5052                 /* Oopsie */
5053                 cm_LockMarkSCacheLost(scp);
5054             }
5055
5056         /* failure here has no bearing on the return value of
5057            cm_Unlock() */
5058         code = 0;
5059
5060     } else if (scp->serverLock != (-1) &&
5061               scp->exclusiveLocks == 0 &&
5062               scp->sharedLocks == 0) {
5063         /* The serverLock should be released entirely */
5064
5065         code = cm_IntReleaseLock(scp, userp, reqp);
5066
5067         if (code == 0) {
5068             scp->serverLock = (-1);
5069         }
5070     }
5071
5072     if (release_userp) {
5073         cm_ReleaseUser(userp);
5074         release_userp = FALSE;
5075     }
5076
5077     if (!exact_match)
5078         goto try_again;         /* might be more than one lock in the range */
5079
5080  done:
5081
5082     osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5083     osi_Log4(afsd_logp, "  leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5084              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5085              (int)(signed char) scp->serverLock);
5086
5087     return code;
5088 }
5089
5090 /* called with scp->rw held */
5091 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5092 {
5093     cm_file_lock_t *fileLock;
5094     osi_queue_t *q;
5095
5096     osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5097
5098     /* cm_scacheLock needed because we are modifying fileLock->flags */
5099     lock_ObtainWrite(&cm_scacheLock);
5100
5101     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5102         fileLock = 
5103             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5104
5105         if (IS_LOCK_ACTIVE(fileLock) &&
5106             !IS_LOCK_CLIENTONLY(fileLock)) {
5107             if (fileLock->lockType == LockRead)
5108                 scp->sharedLocks--;
5109             else
5110                 scp->exclusiveLocks--;
5111
5112             fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5113         }
5114     }
5115
5116     scp->serverLock = -1;
5117     scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5118     lock_ReleaseWrite(&cm_scacheLock);
5119 }
5120
5121 /* Called with no relevant locks held */
5122 void cm_CheckLocks()
5123 {
5124     osi_queue_t *q, *nq;
5125     cm_file_lock_t *fileLock;
5126     cm_req_t req;
5127     AFSFid tfid;
5128     AFSVolSync volSync;
5129     cm_conn_t *connp;
5130     long code;
5131     struct rx_connection * rxconnp;
5132     cm_scache_t * scp;
5133
5134     cm_InitReq(&req);
5135
5136     lock_ObtainWrite(&cm_scacheLock);
5137
5138     cm_lockRefreshCycle++;
5139
5140     osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5141
5142     for (q = cm_allFileLocks; q; q = nq) {
5143         fileLock = (cm_file_lock_t *) q;
5144         nq = osi_QNext(q);
5145         code = -1;
5146
5147         if (IS_LOCK_DELETED(fileLock)) {
5148
5149             osi_QRemove(&cm_allFileLocks, q);
5150             cm_PutFileLock(fileLock);
5151
5152         } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5153
5154             /* Server locks must have been enabled for us to have
5155                received an active non-client-only lock. */
5156             osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5157
5158             scp = fileLock->scp;
5159             osi_assertx(scp != NULL, "null cm_scache_t");
5160
5161             cm_HoldSCacheNoLock(scp);
5162
5163 #ifdef DEBUG
5164             if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5165                 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5166                 osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5167                          fileLock->fid.cell,
5168                          fileLock->fid.volume,
5169                          fileLock->fid.vnode,
5170                          fileLock->fid.unique);
5171                 osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5172                          fileLock->scp->fid.cell,
5173                          fileLock->scp->fid.volume,
5174                          fileLock->scp->fid.vnode,
5175                          fileLock->scp->fid.unique);
5176                 osi_assertx(FALSE, "invalid fid");
5177             }
5178 #endif
5179             /* Server locks are extended once per scp per refresh
5180                cycle. */
5181             if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5182
5183                 int scp_done = FALSE;
5184
5185                 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5186
5187                 lock_ReleaseWrite(&cm_scacheLock);
5188                 lock_ObtainWrite(&scp->rw);
5189
5190                 /* did the lock change while we weren't holding the lock? */
5191                 if (!IS_LOCK_ACTIVE(fileLock))
5192                     goto post_syncopdone;
5193
5194                 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5195                                  CM_SCACHESYNC_NEEDCALLBACK
5196                                  | CM_SCACHESYNC_GETSTATUS
5197                                  | CM_SCACHESYNC_LOCK);
5198
5199                 if (code) {
5200                     osi_Log1(afsd_logp,
5201                              "cm_CheckLocks SyncOp failure code 0x%x", code);
5202                     goto post_syncopdone;
5203                 }
5204
5205                 /* cm_SyncOp releases scp->rw during which the lock
5206                    may get released. */
5207                 if (!IS_LOCK_ACTIVE(fileLock))
5208                     goto pre_syncopdone;
5209
5210                 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5211                     cm_fid_t cfid;
5212                     cm_user_t * userp;
5213
5214                     tfid.Volume = scp->fid.volume;
5215                     tfid.Vnode = scp->fid.vnode;
5216                     tfid.Unique = scp->fid.unique;
5217                     cfid = scp->fid;
5218                     userp = fileLock->userp;
5219                     
5220                     osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d", 
5221                              fileLock,
5222                              scp,
5223                              (int) scp->serverLock);
5224
5225                     lock_ReleaseWrite(&scp->rw);
5226
5227                     do {
5228                         code = cm_ConnFromFID(&cfid, userp,
5229                                        &req, &connp);
5230                         if (code) 
5231                             break;
5232
5233                         rxconnp = cm_GetRxConn(connp);
5234                         code = RXAFS_ExtendLock(rxconnp, &tfid,
5235                                                 &volSync);
5236                         rx_PutConnection(rxconnp);
5237
5238                         osi_Log1(afsd_logp, "   ExtendLock returns %d", code);
5239
5240                     } while (cm_Analyze(connp, userp, &req,
5241                                         &cfid, &volSync, NULL, NULL,
5242                                         code));
5243
5244                     code = cm_MapRPCError(code, &req);
5245
5246                     lock_ObtainWrite(&scp->rw);
5247
5248                     if (code) {
5249                         osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5250                     } else {
5251                         osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5252                         scp->lockDataVersion = scp->dataVersion;
5253                     }
5254
5255                     if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5256                         scp->lockDataVersion == scp->dataVersion) {
5257                         int lockType;
5258
5259                         lockType =
5260                             (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5261
5262                         /* we might still have a chance to obtain a
5263                            new lock */
5264
5265                         code = cm_IntSetLock(scp, userp, lockType, &req);
5266
5267                         if (code) {
5268                             code = CM_ERROR_INVAL;
5269                         } else if (scp->lockDataVersion != scp->dataVersion) {
5270
5271                             /* now check if we still have the file at
5272                                the right data version. */
5273                             osi_Log1(afsd_logp,
5274                                      "Data version mismatch on scp 0x%p",
5275                                      scp);
5276                             osi_Log2(afsd_logp,
5277                                      "   Data versions: before=%I64d, after=%I64d",
5278                                      scp->lockDataVersion,
5279                                      scp->dataVersion);
5280
5281                             code = cm_IntReleaseLock(scp, userp, &req);
5282
5283                             code = CM_ERROR_INVAL;
5284                         }
5285                     }
5286
5287                     if (code == EINVAL || code == CM_ERROR_INVAL ||
5288                         code == CM_ERROR_BADFD) {
5289                         cm_LockMarkSCacheLost(scp);
5290                     }
5291
5292                 } else {
5293                     /* interestingly, we have found an active lock
5294                        belonging to an scache that has no
5295                        serverLock */
5296                     cm_LockMarkSCacheLost(scp);
5297                 }
5298
5299                 scp_done = TRUE;
5300
5301             pre_syncopdone:
5302
5303                 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5304
5305             post_syncopdone:
5306                 lock_ReleaseWrite(&scp->rw);
5307
5308                 lock_ObtainWrite(&cm_scacheLock);
5309
5310                 if (code == 0) {
5311                     fileLock->lastUpdate = time(NULL);
5312                 }
5313                 
5314                 if (scp_done)
5315                     scp->lastRefreshCycle = cm_lockRefreshCycle;
5316
5317             } else {
5318                 /* we have already refreshed the locks on this scp */
5319                 fileLock->lastUpdate = time(NULL);
5320             }
5321
5322             cm_ReleaseSCacheNoLock(scp);
5323
5324         } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5325             /* TODO: Check callbacks */
5326         }
5327     }
5328
5329     lock_ReleaseWrite(&cm_scacheLock);
5330     osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5331 }
5332
5333 /* NOT called with scp->rw held. */
5334 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5335 {
5336     long code = 0;
5337     cm_scache_t *scp = NULL;
5338     cm_file_lock_t *fileLock;
5339     osi_queue_t *q;
5340     cm_req_t req;
5341     int newLock = -1;
5342     int force_client_lock = FALSE;
5343     int has_insert = FALSE;
5344     int check_data_version = FALSE;
5345
5346     cm_InitReq(&req);
5347
5348     if (client_is_dead) {
5349         code = CM_ERROR_TIMEDOUT;
5350         goto updateLock;
5351     }
5352
5353     lock_ObtainRead(&cm_scacheLock);
5354
5355     osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5356     osi_Log4(afsd_logp, "    offset(%x:%x) length(%x:%x)",
5357              (unsigned)(oldFileLock->range.offset >> 32),
5358              (unsigned)(oldFileLock->range.offset & 0xffffffff),
5359              (unsigned)(oldFileLock->range.length >> 32),
5360              (unsigned)(oldFileLock->range.length & 0xffffffff));
5361     osi_Log4(afsd_logp, "    key<0x%x,0x%x,0x%x> flags=%x",
5362              oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5363              (unsigned)(oldFileLock->flags));
5364
5365     /* if the lock has already been granted, then we have nothing to do */
5366     if (IS_LOCK_ACTIVE(oldFileLock)) {
5367         lock_ReleaseRead(&cm_scacheLock);
5368         osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5369         return 0;
5370     }
5371
5372     /* we can't do anything with lost or deleted locks at the moment. */
5373     if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5374         code = CM_ERROR_BADFD;
5375         osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5376         lock_ReleaseRead(&cm_scacheLock);
5377         goto updateLock;
5378     }
5379
5380     scp = oldFileLock->scp;
5381
5382     osi_assertx(scp != NULL, "null cm_scache_t");
5383
5384     lock_ReleaseRead(&cm_scacheLock);
5385     lock_ObtainWrite(&scp->rw);
5386
5387     code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5388                              oldFileLock->userp,
5389                              &req, &has_insert);
5390
5391     if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5392         if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5393         force_client_lock = TRUE;
5394         }
5395         code = 0;
5396     } else if (code) {
5397         lock_ReleaseWrite(&scp->rw);
5398         return code;
5399     }
5400
5401     lock_ObtainWrite(&cm_scacheLock);
5402
5403     /* Check if we already have a sufficient server lock to allow this
5404        lock to go through. */
5405     if (IS_LOCK_WAITLOCK(oldFileLock) &&
5406         (!SERVERLOCKS_ENABLED(scp) ||
5407          scp->serverLock == oldFileLock->lockType ||
5408          scp->serverLock == LockWrite)) {
5409
5410         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5411
5412         if (SERVERLOCKS_ENABLED(scp)) {
5413             osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock.  Granting",
5414                      (int) scp->serverLock);
5415         } else {
5416             osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5417         }
5418
5419         lock_ReleaseWrite(&cm_scacheLock);
5420         lock_ReleaseWrite(&scp->rw);
5421
5422         return 0;
5423     }
5424
5425     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5426
5427         /* check if the conflicting locks have dissappeared already */
5428         for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5429
5430             fileLock = (cm_file_lock_t *)
5431                 ((char *) q - offsetof(cm_file_lock_t, fileq));
5432
5433             if (IS_LOCK_LOST(fileLock)) {
5434                 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5435                     code = CM_ERROR_BADFD;
5436                     oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5437                     osi_Log1(afsd_logp, "    found lost lock %p for same key.  Marking lock as lost",
5438                              fileLock);
5439                     break;
5440                 } else if (fileLock->lockType == LockWrite &&
5441                            INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5442                     osi_Log1(afsd_logp, "    found conflicting LOST lock %p", fileLock);
5443                     code = CM_ERROR_WOULDBLOCK;
5444                     break;
5445                 }
5446             }
5447
5448             if (IS_LOCK_ACCEPTED(fileLock) &&
5449                 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5450
5451                 if (oldFileLock->lockType != LockRead ||
5452                    fileLock->lockType != LockRead) {
5453
5454                     osi_Log1(afsd_logp, "    found conflicting lock %p", fileLock);
5455                     code = CM_ERROR_WOULDBLOCK;
5456                     break;
5457                 }
5458             }
5459         }
5460     }
5461
5462     if (code != 0) {
5463         lock_ReleaseWrite(&cm_scacheLock);
5464         lock_ReleaseWrite(&scp->rw);
5465
5466         goto handleCode;
5467     }
5468
5469     /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5470        If it is WAITUNLOCK, then we didn't find any conflicting lock
5471        but we haven't verfied whether the serverLock is sufficient to
5472        assert it.  If it is WAITLOCK, then the serverLock is
5473        insufficient to assert it. Eitherway, we are ready to accept
5474        the lock as either ACTIVE or WAITLOCK depending on the
5475        serverLock. */
5476
5477     /* First, promote the WAITUNLOCK to a WAITLOCK */
5478     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5479         if (oldFileLock->lockType == LockRead)
5480             scp->sharedLocks++;
5481         else
5482             scp->exclusiveLocks++;
5483
5484         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5485         oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5486     }
5487
5488     osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5489
5490     if (force_client_lock ||
5491         !SERVERLOCKS_ENABLED(scp) ||
5492         scp->serverLock == oldFileLock->lockType ||
5493         (oldFileLock->lockType == LockRead &&
5494          scp->serverLock == LockWrite)) {
5495
5496         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5497
5498         if ((force_client_lock ||
5499              !SERVERLOCKS_ENABLED(scp)) &&
5500             !IS_LOCK_CLIENTONLY(oldFileLock)) {
5501
5502             oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5503
5504             if (oldFileLock->lockType == LockRead)
5505                 scp->sharedLocks--;
5506             else
5507                 scp->exclusiveLocks--;
5508
5509             scp->clientLocks++;
5510         }
5511
5512         lock_ReleaseWrite(&cm_scacheLock);
5513         lock_ReleaseWrite(&scp->rw);
5514
5515         return 0;
5516
5517     } else {
5518         cm_user_t * userp;
5519
5520         code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5521                          CM_SCACHESYNC_NEEDCALLBACK
5522                          | CM_SCACHESYNC_GETSTATUS
5523                          | CM_SCACHESYNC_LOCK);
5524         if (code) {
5525             osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5526             lock_ReleaseWrite(&cm_scacheLock);
5527             goto post_syncopdone;
5528         }
5529
5530         if (!IS_LOCK_WAITLOCK(oldFileLock))
5531             goto pre_syncopdone;
5532
5533         userp = oldFileLock->userp;
5534
5535 #ifndef AGGRESSIVE_LOCKS
5536         newLock = oldFileLock->lockType;
5537 #else
5538         newLock = LockWrite;
5539 #endif
5540
5541         if (has_insert) {
5542             /* if has_insert is non-zero, then:
5543                - the lock a LockRead
5544                - we don't have permission to get a LockRead
5545                - we do have permission to get a LockWrite
5546                - the server supports VICED_CAPABILITY_WRITELOCKACL
5547             */
5548
5549             newLock = LockWrite;
5550         }
5551
5552         lock_ReleaseWrite(&cm_scacheLock);
5553
5554         /* when we get here, either we have a read-lock and want a
5555            write-lock or we don't have any locks and we want some
5556            lock. */
5557
5558         if (scp->serverLock == LockRead) {
5559
5560             osi_assertx(newLock == LockWrite, "!LockWrite");
5561
5562             osi_Log0(afsd_logp, "  Attempting to UPGRADE from LockRead to LockWrite");
5563
5564             scp->lockDataVersion = scp->dataVersion;
5565             check_data_version = TRUE;
5566
5567             code = cm_IntReleaseLock(scp, userp, &req);
5568
5569             if (code)
5570                 goto pre_syncopdone;
5571             else
5572                 scp->serverLock = -1;
5573         }
5574
5575         code = cm_IntSetLock(scp, userp, newLock, &req);
5576
5577         if (code == 0) {
5578             if (scp->dataVersion != scp->lockDataVersion) {
5579                 /* we lost a race.  too bad */
5580
5581                 osi_Log0(afsd_logp,
5582                          "  Data version mismatch while upgrading lock.");
5583                 osi_Log2(afsd_logp,
5584                          "  Data versions before=%I64d, after=%I64d",
5585                          scp->lockDataVersion,
5586                          scp->dataVersion);
5587                 osi_Log1(afsd_logp,
5588                          "  Releasing stale lock for scp 0x%x", scp);
5589
5590                 code = cm_IntReleaseLock(scp, userp, &req);
5591
5592                 scp->serverLock = -1;
5593
5594                 code = CM_ERROR_INVAL;
5595
5596                 cm_LockMarkSCacheLost(scp);
5597             } else {
5598                 scp->serverLock = newLock;
5599             }
5600         }
5601
5602     pre_syncopdone:
5603         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5604     post_syncopdone:
5605         ;
5606     }
5607
5608   handleCode:
5609     if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5610         lock_ObtainWrite(&cm_scacheLock);
5611         if (scp->fileLocksT == &oldFileLock->fileq)
5612             scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5613         osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5614         lock_ReleaseWrite(&cm_scacheLock);
5615     }
5616     lock_ReleaseWrite(&scp->rw);
5617
5618   updateLock:
5619     lock_ObtainWrite(&cm_scacheLock);
5620     if (code == 0) {
5621         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5622     } else if (code != CM_ERROR_WOULDBLOCK) {
5623         oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5624         cm_ReleaseUser(oldFileLock->userp);
5625         oldFileLock->userp = NULL;
5626         if (oldFileLock->scp) {
5627             cm_ReleaseSCacheNoLock(oldFileLock->scp);
5628             oldFileLock->scp = NULL;
5629         }
5630     }
5631     lock_ReleaseWrite(&cm_scacheLock);
5632
5633     return code;
5634 }
5635
5636 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
5637 {
5638     cm_key_t key;
5639
5640     key.process_id = process_id;
5641     key.session_id = session_id;
5642     key.file_id = file_id;
5643
5644     return key;
5645 }
5646
5647 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5648 {
5649     return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5650         ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
5651 }
5652
5653 void cm_ReleaseAllLocks(void)
5654 {
5655     cm_scache_t *scp;
5656     cm_req_t req;
5657     cm_user_t *userp;
5658     cm_key_t   key;
5659     cm_file_lock_t *fileLock;
5660     unsigned int i;
5661
5662     for (i = 0; i < cm_data.scacheHashTableSize; i++)
5663     {
5664         for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5665             while (scp->fileLocksH != NULL) {
5666                 lock_ObtainWrite(&scp->rw);
5667                 lock_ObtainWrite(&cm_scacheLock);
5668                 if (!scp->fileLocksH) {
5669                     lock_ReleaseWrite(&cm_scacheLock);
5670                     lock_ReleaseWrite(&scp->rw);
5671                     break;
5672                 }
5673                 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5674                 userp = fileLock->userp;
5675                 cm_HoldUser(userp);
5676                 key = fileLock->key;
5677                 cm_HoldSCacheNoLock(scp);
5678                 lock_ReleaseWrite(&cm_scacheLock);
5679                 cm_UnlockByKey(scp, key, 0, userp, &req);
5680                 cm_ReleaseSCache(scp);
5681                 cm_ReleaseUser(userp);
5682                 lock_ReleaseWrite(&scp->rw);
5683             }
5684         }
5685     }
5686 }