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