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