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