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