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