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