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