efbf2b3c6dc1488d072e467af52e44f72b1c9947
[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_NOSUCHFILE;
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_NOSUCHFILE;
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     lock_ObtainWrite(&dscp->dirlock);
1623     dirop.lockType = CM_DIRLOCK_WRITE;
1624     lock_ObtainMutex(&dscp->mx);
1625     cm_dnlcRemove(dscp, namep);
1626     cm_SyncOpDone(dscp, NULL, sflags);
1627     if (code == 0) {
1628         cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, 0);
1629     } else if (code == CM_ERROR_NOSUCHFILE) {
1630         /* windows would not have allowed the request to delete the file 
1631          * if it did not believe the file existed.  therefore, we must 
1632          * have an inconsistent view of the world.
1633          */
1634         dscp->cbServerp = NULL;
1635     }
1636     lock_ReleaseMutex(&dscp->mx);
1637
1638     if (code == 0 && cm_CheckDirOpForSingleChange(&dirop)) {
1639         cm_DirDeleteEntry(&dirop, namep);
1640 #ifdef USE_BPLUS
1641         cm_BPlusDirDeleteEntry(&dirop, namep);
1642 #endif
1643     }
1644     cm_EndDirOp(&dirop);
1645
1646     return code;
1647 }
1648
1649 /* called with a locked vnode, and fills in the link info.
1650  * returns this the vnode still locked.
1651  */
1652 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1653 {
1654     long code;
1655     cm_buf_t *bufp;
1656     long temp;
1657     osi_hyper_t thyper;
1658
1659     lock_AssertMutex(&linkScp->mx);
1660     if (!linkScp->mountPointStringp[0]) {
1661         /* read the link data */
1662         lock_ReleaseMutex(&linkScp->mx);
1663         thyper.LowPart = thyper.HighPart = 0;
1664         code = buf_Get(linkScp, &thyper, &bufp);
1665         lock_ObtainMutex(&linkScp->mx);
1666         if (code) 
1667             return code;
1668         while (1) {
1669             code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1670                               CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1671             if (code) {
1672                 buf_Release(bufp);
1673                 return code;
1674             }
1675             cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1676
1677             if (cm_HaveBuffer(linkScp, bufp, 0)) 
1678                 break;
1679
1680             code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1681             if (code) {
1682                 buf_Release(bufp);
1683                 return code;
1684             }
1685         } /* while loop to get the data */
1686                 
1687         /* now if we still have no link read in,
1688          * copy the data from the buffer */
1689         if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1690             buf_Release(bufp);
1691             return CM_ERROR_TOOBIG;
1692         }
1693
1694         /* otherwise, it fits; make sure it is still null (could have
1695          * lost race with someone else referencing this link above),
1696          * and if so, copy in the data.
1697          */
1698         if (!linkScp->mountPointStringp[0]) {
1699             strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1700             linkScp->mountPointStringp[temp] = 0;       /* null terminate */
1701         }
1702         buf_Release(bufp);
1703     }   /* don't have sym link contents cached */
1704
1705     return 0;
1706 }       
1707
1708 /* called with a held vnode and a path suffix, with the held vnode being a
1709  * symbolic link.  Our goal is to generate a new path to interpret, and return
1710  * this new path in newSpaceBufferp.  If the new vnode is relative to a dir
1711  * other than the directory containing the symbolic link, then the new root is
1712  * returned in *newRootScpp, otherwise a null is returned there.
1713  */
1714 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1715                       cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1716                       cm_user_t *userp, cm_req_t *reqp)
1717 {
1718     long code = 0;
1719     long len;
1720     char *linkp;
1721     cm_space_t *tsp;
1722
1723     lock_ObtainMutex(&linkScp->mx);
1724     code = cm_HandleLink(linkScp, userp, reqp);
1725     if (code) 
1726         goto done;
1727
1728     /* if we may overflow the buffer, bail out; buffer is signficantly
1729      * bigger than max path length, so we don't really have to worry about
1730      * being a little conservative here.
1731      */
1732     if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1733          >= CM_UTILS_SPACESIZE)
1734         return CM_ERROR_TOOBIG;
1735
1736     tsp = cm_GetSpace();
1737     linkp = linkScp->mountPointStringp;
1738     if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1739         if (strlen(linkp) > cm_mountRootLen)
1740             strcpy(tsp->data, linkp+cm_mountRootLen+1);
1741         else
1742             tsp->data[0] = 0;
1743         *newRootScpp = cm_data.rootSCachep;
1744         cm_HoldSCache(cm_data.rootSCachep);
1745     } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1746         if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName)))) 
1747         {
1748             char * p = &linkp[len + 3];
1749             if (strnicmp(p, "all", 3) == 0)
1750                 p += 4;
1751
1752             strcpy(tsp->data, p);
1753             for (p = tsp->data; *p; p++) {
1754                 if (*p == '\\')
1755                     *p = '/';
1756             }
1757             *newRootScpp = cm_data.rootSCachep;
1758             cm_HoldSCache(cm_data.rootSCachep);
1759         } else {
1760             linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1761             strcpy(tsp->data, linkp);
1762             *newRootScpp = NULL;
1763             code = CM_ERROR_PATH_NOT_COVERED;
1764         }
1765     } else if ( !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1766         linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1767         strcpy(tsp->data, linkp);
1768         *newRootScpp = NULL;
1769         code = CM_ERROR_PATH_NOT_COVERED;
1770     } else if (*linkp == '\\' || *linkp == '/') {
1771 #if 0   
1772         /* formerly, this was considered to be from the AFS root,
1773          * but this seems to create problems.  instead, we will just
1774          * reject the link */
1775         strcpy(tsp->data, linkp+1);
1776         *newRootScpp = cm_data.rootSCachep;
1777         cm_HoldSCache(cm_data.rootSCachep);
1778 #else
1779         /* we still copy the link data into the response so that 
1780          * the user can see what the link points to
1781          */
1782         linkScp->fileType = CM_SCACHETYPE_INVALID;
1783         strcpy(tsp->data, linkp);
1784         *newRootScpp = NULL;
1785         code = CM_ERROR_NOSUCHPATH;
1786 #endif  
1787     } else {
1788         /* a relative link */
1789         strcpy(tsp->data, linkp);
1790         *newRootScpp = NULL;
1791     }
1792     if (pathSuffixp[0] != 0) {  /* if suffix string is non-null */
1793         strcat(tsp->data, "\\");
1794         strcat(tsp->data, pathSuffixp);
1795     }
1796     *newSpaceBufferp = tsp;
1797
1798   done:
1799     lock_ReleaseMutex(&linkScp->mx);
1800     return code;
1801 }
1802 #ifdef DEBUG_REFCOUNT
1803 long cm_NameIDbg(cm_scache_t *rootSCachep, char *pathp, long flags,
1804                cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp, 
1805                char * file, long line)
1806 #else
1807 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1808                cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1809 #endif
1810 {
1811     long code;
1812     char *tp;                   /* ptr moving through input buffer */
1813     char tc;                    /* temp char */
1814     int haveComponent;          /* has new component started? */
1815     char component[256];        /* this is the new component */
1816     char *cp;                   /* component name being assembled */
1817     cm_scache_t *tscp;          /* current location in the hierarchy */
1818     cm_scache_t *nscp;          /* next dude down */
1819     cm_scache_t *dirScp;        /* last dir we searched */
1820     cm_scache_t *linkScp;       /* new root for the symlink we just
1821     * looked up */
1822     cm_space_t *psp;            /* space for current path, if we've hit
1823     * any symlinks */
1824     cm_space_t *tempsp;         /* temp vbl */
1825     char *restp;                /* rest of the pathname to interpret */
1826     int symlinkCount;           /* count of # of symlinks traversed */
1827     int extraFlag;              /* avoid chasing mt pts for dir cmd */
1828     int phase = 1;              /* 1 = tidPathp, 2 = pathp */
1829 #define MAX_FID_COUNT 512
1830     cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1831     int fid_count = 0;          /* number of fids processed in this path walk */
1832     int i;
1833
1834 #ifdef DEBUG_REFCOUNT
1835     afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1836     osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %s tidpath %s flags 0x%x",
1837               rootSCachep, pathp ? pathp : "<NULL>", tidPathp ? tidPathp : "<NULL>", 
1838               flags);
1839 #endif
1840
1841     tp = tidPathp;
1842     if (tp == NULL) {
1843         tp = pathp;
1844         phase = 2;
1845     }
1846     if (tp == NULL) {
1847         tp = "";
1848     }
1849     haveComponent = 0;
1850     psp = NULL;
1851     tscp = rootSCachep;
1852     cm_HoldSCache(tscp);
1853     symlinkCount = 0;
1854     dirScp = NULL;
1855
1856
1857     while (1) {
1858         tc = *tp++;
1859
1860         /* map Unix slashes into DOS ones so we can interpret Unix
1861          * symlinks properly
1862          */
1863         if (tc == '/') 
1864             tc = '\\';
1865
1866         if (!haveComponent) {
1867             if (tc == '\\') {
1868                 continue;
1869             } else if (tc == 0) {
1870                 if (phase == 1) {
1871                     phase = 2;
1872                     tp = pathp;
1873                     continue;
1874                 }
1875                 code = 0;
1876                 break;
1877             } else {
1878                 haveComponent = 1;
1879                 cp = component;
1880                 *cp++ = tc;
1881             }
1882         } else {
1883             /* we have a component here */
1884             if (tc == 0 || tc == '\\') {
1885                 /* end of the component; we're at the last
1886                  * component if tc == 0.  However, if the last
1887                  * is a symlink, we have more to do.
1888                  */
1889                 *cp++ = 0;      /* add null termination */
1890                 extraFlag = 0;
1891                 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1892                     extraFlag = CM_FLAG_NOMOUNTCHASE;
1893                 code = cm_Lookup(tscp, component,
1894                                   flags | extraFlag,
1895                                   userp, reqp, &nscp);
1896
1897                 if (code == 0) {
1898                     if (!strcmp(component,"..") || !strcmp(component,".")) {
1899                         /* 
1900                          * roll back the fid list until we find the fid 
1901                          * that matches where we are now.  Its not necessarily
1902                          * one or two fids because they might have been 
1903                          * symlinks or mount points or both that were crossed.  
1904                          */
1905                         for ( i=fid_count-1; i>=0; i--) {
1906                             if (!cm_FidCmp(&nscp->fid, &fids[i]))
1907                                 break;
1908                         }
1909                     } else {
1910                         /* add the new fid to the list */
1911                         for ( i=0; i<fid_count; i++) {
1912                             if ( !cm_FidCmp(&nscp->fid, &fids[i]) ) {
1913                                 code = CM_ERROR_TOO_MANY_SYMLINKS;
1914                                 cm_ReleaseSCache(nscp);
1915                                 nscp = NULL;
1916                                 break;
1917                             }
1918                         }
1919                         if (i == fid_count && fid_count < MAX_FID_COUNT) {
1920                             fids[fid_count++] = nscp->fid;
1921                         }
1922                     }
1923                 }
1924
1925                 if (code) {
1926                     cm_ReleaseSCache(tscp);
1927                     if (dirScp)
1928                         cm_ReleaseSCache(dirScp);
1929                     if (psp) 
1930                         cm_FreeSpace(psp);
1931                     if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1932                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1933                         return CM_ERROR_NOSUCHPATH;
1934                     } else {
1935                         osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1936                         return code;
1937                     }
1938                 }
1939
1940                 haveComponent = 0;      /* component done */
1941                 if (dirScp)
1942                     cm_ReleaseSCache(dirScp);
1943                 dirScp = tscp;          /* for some symlinks */
1944                 tscp = nscp;            /* already held */
1945                 nscp = NULL;
1946                 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1947                     code = 0;
1948                     if (dirScp) {
1949                         cm_ReleaseSCache(dirScp);
1950                         dirScp = NULL;
1951                     }
1952                     break;
1953                 }
1954
1955                 /* now, if tscp is a symlink, we should follow
1956                  * it and assemble the path again.
1957                  */
1958                 lock_ObtainMutex(&tscp->mx);
1959                 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1960                                   CM_SCACHESYNC_GETSTATUS
1961                                   | CM_SCACHESYNC_NEEDCALLBACK);
1962                 if (code) {
1963                     lock_ReleaseMutex(&tscp->mx);
1964                     cm_ReleaseSCache(tscp);
1965                     tscp = NULL;
1966                     if (dirScp) {
1967                         cm_ReleaseSCache(dirScp);
1968                         dirScp = NULL;
1969                     }
1970                     break;
1971                 }
1972                 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1973
1974                 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1975                     /* this is a symlink; assemble a new buffer */
1976                     lock_ReleaseMutex(&tscp->mx);
1977                     if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1978                         cm_ReleaseSCache(tscp);
1979                         tscp = NULL;
1980                         if (dirScp) {
1981                             cm_ReleaseSCache(dirScp);
1982                             dirScp = NULL;
1983                         }
1984                         if (psp) 
1985                             cm_FreeSpace(psp);
1986                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
1987                         return CM_ERROR_TOO_MANY_SYMLINKS;
1988                     }
1989                     if (tc == 0) 
1990                         restp = "";
1991                     else 
1992                         restp = tp;
1993                     code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1994
1995                     if (code == 0 && linkScp != NULL) {
1996                         if (linkScp == cm_data.rootSCachep) 
1997                             fid_count = 0;
1998                         else {
1999                             for ( i=0; i<fid_count; i++) {
2000                                 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2001                                     code = CM_ERROR_TOO_MANY_SYMLINKS;
2002                                     cm_ReleaseSCache(linkScp);
2003                                     nscp = NULL;
2004                                     break;
2005                                 }
2006                             }
2007                         }
2008                         if (i == fid_count && fid_count < MAX_FID_COUNT) {
2009                             fids[fid_count++] = linkScp->fid;
2010                         }
2011                     }
2012
2013                     if (code) {
2014                         /* something went wrong */
2015                         cm_ReleaseSCache(tscp);
2016                         tscp = NULL;
2017                         if (dirScp) {
2018                             cm_ReleaseSCache(dirScp);
2019                             dirScp = NULL;
2020                         }
2021                         break;
2022                     }
2023
2024                     /* otherwise, tempsp has the new path,
2025                      * and linkScp is the new root from
2026                      * which to interpret that path.
2027                      * Continue with the namei processing,
2028                      * also doing the bookkeeping for the
2029                      * space allocation and tracking the
2030                      * vnode reference counts.
2031                      */
2032                     if (psp) 
2033                         cm_FreeSpace(psp);
2034                     psp = tempsp;
2035                     tp = psp->data;
2036                     cm_ReleaseSCache(tscp);
2037                     tscp = linkScp;
2038                     linkScp = NULL;
2039                     /* already held
2040                      * by AssembleLink
2041                      * now, if linkScp is null, that's
2042                      * AssembleLink's way of telling us that
2043                      * the sym link is relative to the dir
2044                      * containing the link.  We have a ref
2045                      * to it in dirScp, and we hold it now
2046                      * and reuse it as the new spot in the
2047                      * dir hierarchy.
2048                      */
2049                     if (tscp == NULL) {
2050                         tscp = dirScp;
2051                         dirScp = NULL;
2052                     }
2053                 } else {
2054                     /* not a symlink, we may be done */
2055                     lock_ReleaseMutex(&tscp->mx);
2056                     if (tc == 0) {
2057                         if (phase == 1) {
2058                             phase = 2;
2059                             tp = pathp;
2060                             continue;
2061                         }
2062                         if (dirScp) {
2063                             cm_ReleaseSCache(dirScp);
2064                             dirScp = NULL;
2065                         }
2066                         code = 0;
2067                         break;
2068                     }
2069                 }
2070                 if (dirScp) {
2071                     cm_ReleaseSCache(dirScp);
2072                     dirScp = NULL;
2073                 }
2074             } /* end of a component */
2075             else 
2076                 *cp++ = tc;
2077         } /* we have a component */
2078     } /* big while loop over all components */
2079
2080     /* already held */
2081     if (dirScp)
2082         cm_ReleaseSCache(dirScp);
2083     if (psp) 
2084         cm_FreeSpace(psp);
2085     if (code == 0) 
2086         *outScpp = tscp;
2087     else if (tscp)
2088         cm_ReleaseSCache(tscp);
2089
2090 #ifdef DEBUG_REFCOUNT
2091     afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp)->refCount);
2092 #endif
2093     osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2094     return code;
2095 }
2096
2097 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2098  * We chase the link, and return a held pointer to the target, if it exists,
2099  * in *outScpp.  If we succeed, we return 0, otherwise we return an error code
2100  * and do not hold or return a target vnode.
2101  *
2102  * This is very similar to calling cm_NameI with the last component of a name,
2103  * which happens to be a symlink, except that we've already passed by the name.
2104  *
2105  * This function is typically called by the directory listing functions, which
2106  * encounter symlinks but need to return the proper file length so programs
2107  * like "more" work properly when they make use of the attributes retrieved from
2108  * the dir listing.
2109  *
2110  * The input vnode should not be locked when this function is called.
2111  */
2112 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2113                          cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2114 {
2115     long code;
2116     cm_space_t *spacep;
2117     cm_scache_t *newRootScp;
2118
2119     osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2120
2121     code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2122     if (code) 
2123         return code;
2124
2125     /* now, if newRootScp is NULL, we're really being told that the symlink
2126      * is relative to the current directory (dscp).
2127      */
2128     if (newRootScp == NULL) {
2129         newRootScp = dscp;
2130         cm_HoldSCache(dscp);
2131     }
2132
2133     code = cm_NameI(newRootScp, spacep->data,
2134                      CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2135                      userp, NULL, reqp, outScpp);
2136
2137     if (code == CM_ERROR_NOSUCHFILE)
2138         code = CM_ERROR_NOSUCHPATH;
2139
2140     /* this stuff is allocated no matter what happened on the namei call,
2141      * so free it */
2142     cm_FreeSpace(spacep);
2143     cm_ReleaseSCache(newRootScp);
2144
2145     if (linkScp == *outScpp) {
2146         cm_ReleaseSCache(*outScpp);
2147         *outScpp = NULL;
2148         code = CM_ERROR_NOSUCHPATH;
2149     }
2150
2151     return code;
2152 }
2153
2154 /* make this big enough so that one buffer of dir pages won't overflow.  We'll
2155  * check anyway, but we want to minimize the chance that we have to leave stuff
2156  * unstat'd.
2157  */
2158 #define CM_BULKMAX              (3 * AFSCBMAX)
2159
2160 /* rock for bulk stat calls */
2161 typedef struct cm_bulkStat {
2162     osi_hyper_t bufOffset;      /* only do it for things in this buffer page */
2163
2164     /* info for the actual call */
2165     int counter;                        /* next free slot */
2166     AFSFid fids[CM_BULKMAX];
2167     AFSFetchStatus stats[CM_BULKMAX];
2168     AFSCallBack callbacks[CM_BULKMAX];
2169 } cm_bulkStat_t;
2170
2171 /* for a given entry, make sure that it isn't in the stat cache, and then
2172  * add it to the list of file IDs to be obtained.
2173  *
2174  * Don't bother adding it if we already have a vnode.  Note that the dir
2175  * is locked, so we have to be careful checking the vnode we're thinking of
2176  * processing, to avoid deadlocks.
2177  */
2178 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2179                      osi_hyper_t *offp)
2180 {
2181     osi_hyper_t thyper;
2182     cm_bulkStat_t *bsp;
2183     int i;
2184     cm_scache_t *tscp;
2185     cm_fid_t tfid;
2186
2187     bsp = rockp;
2188
2189     /* Don't overflow bsp. */
2190     if (bsp->counter >= CM_BULKMAX)
2191         return CM_ERROR_STOPNOW;
2192
2193     thyper.LowPart = cm_data.buf_blockSize;
2194     thyper.HighPart = 0;
2195     thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2196
2197     /* thyper is now the first byte past the end of the record we're
2198      * interested in, and bsp->bufOffset is the first byte of the record
2199      * we're interested in.
2200      * Skip data in the others.
2201      * Skip '.' and '..'
2202      */
2203     if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2204         return 0;
2205     if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2206         return CM_ERROR_STOPNOW;
2207     if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2208         return 0;
2209
2210     tfid.cell = scp->fid.cell;
2211     tfid.volume = scp->fid.volume;
2212     tfid.vnode = ntohl(dep->fid.vnode);
2213     tfid.unique = ntohl(dep->fid.unique);
2214     tscp = cm_FindSCache(&tfid);
2215     if (tscp) {
2216         if (lock_TryMutex(&tscp->mx)) {
2217             /* we have an entry that we can look at */
2218             if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2219                 /* we have a callback on it.  Don't bother
2220                  * fetching this stat entry, since we're happy
2221                  * with the info we have.
2222                  */
2223                 lock_ReleaseMutex(&tscp->mx);
2224                 cm_ReleaseSCache(tscp);
2225                 return 0;
2226             }
2227             lock_ReleaseMutex(&tscp->mx);
2228         }       /* got lock */
2229         cm_ReleaseSCache(tscp);
2230     }   /* found entry */
2231
2232 #ifdef AFS_FREELANCE_CLIENT
2233     // yj: if this is a mountpoint under root.afs then we don't want it
2234     // to be bulkstat-ed, instead, we call getSCache directly and under
2235     // getSCache, it is handled specially.
2236     if  ( cm_freelanceEnabled &&
2237           tfid.cell==AFS_FAKE_ROOT_CELL_ID && 
2238           tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2239           !(tfid.vnode==0x1 && tfid.unique==0x1) )
2240     {       
2241         osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2242         return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2243     }
2244 #endif /* AFS_FREELANCE_CLIENT */
2245
2246     i = bsp->counter++;
2247     bsp->fids[i].Volume = scp->fid.volume;
2248     bsp->fids[i].Vnode = tfid.vnode;
2249     bsp->fids[i].Unique = tfid.unique;
2250     return 0;
2251 }       
2252
2253 /* called with a locked scp and a pointer to a buffer.  Make bulk stat
2254  * calls on all undeleted files in the page of the directory specified.
2255  */
2256 afs_int32
2257 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2258                cm_req_t *reqp)
2259 {
2260     long code;
2261     cm_bulkStat_t bb;   /* this is *BIG*, probably 16K or so;
2262                          * watch for stack problems */
2263     AFSCBFids fidStruct;
2264     AFSBulkStats statStruct;
2265     cm_conn_t *connp;
2266     AFSCBs callbackStruct;
2267     long filex;
2268     AFSVolSync volSync;
2269     cm_callbackRequest_t cbReq;
2270     long filesThisCall;
2271     long i;
2272     long j;
2273     cm_scache_t *scp;
2274     cm_fid_t tfid;
2275     struct rx_connection * callp;
2276     int inlinebulk = 0;         /* Did we use InlineBulkStatus RPC or not? */
2277
2278     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2279
2280     /* should be on a buffer boundary */
2281     osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
2282
2283     memset(&bb, 0, sizeof(bb));
2284     bb.bufOffset = *offsetp;
2285
2286     lock_ReleaseMutex(&dscp->mx);
2287     /* first, assemble the file IDs we need to stat */
2288     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
2289
2290     /* if we failed, bail out early */
2291     if (code && code != CM_ERROR_STOPNOW) {
2292         lock_ObtainMutex(&dscp->mx);
2293         return code;
2294     }
2295
2296     /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2297      * make the calls to create the entries.  Handle AFSCBMAX files at a
2298      * time.
2299      */
2300     filex = 0;
2301     while (filex < bb.counter) {
2302         filesThisCall = bb.counter - filex;
2303         if (filesThisCall > AFSCBMAX) 
2304             filesThisCall = AFSCBMAX;
2305
2306         fidStruct.AFSCBFids_len = filesThisCall;
2307         fidStruct.AFSCBFids_val = &bb.fids[filex];
2308         statStruct.AFSBulkStats_len = filesThisCall;
2309         statStruct.AFSBulkStats_val = &bb.stats[filex];
2310         callbackStruct.AFSCBs_len = filesThisCall;
2311         callbackStruct.AFSCBs_val = &bb.callbacks[filex];
2312         cm_StartCallbackGrantingCall(NULL, &cbReq);
2313         osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2314         do {
2315             code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2316             if (code) 
2317                 continue;
2318
2319             callp = cm_GetRxConn(connp);
2320             if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2321                 code = RXAFS_InlineBulkStatus(callp, &fidStruct,
2322                                      &statStruct, &callbackStruct, &volSync);
2323                 if (code == RXGEN_OPCODE) {
2324                     cm_SetServerNoInlineBulk(connp->serverp, 0);
2325                 } else {
2326                     inlinebulk = 1;
2327                 }
2328             }
2329             if (!inlinebulk) {
2330                 code = RXAFS_BulkStatus(callp, &fidStruct,
2331                                         &statStruct, &callbackStruct, &volSync);
2332             }
2333             rx_PutConnection(callp);
2334
2335         } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2336                              &volSync, NULL, &cbReq, code));
2337         code = cm_MapRPCError(code, reqp);
2338         if (code)
2339             osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x", 
2340                       inlinebulk ? "Inline" : "", code);
2341         else
2342             osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2343
2344         /* may as well quit on an error, since we're not going to do
2345          * much better on the next immediate call, either.
2346          */
2347         if (code) {
2348             cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2349             break;
2350         }
2351
2352         /* otherwise, we should do the merges */
2353         for (i = 0; i<filesThisCall; i++) {
2354             j = filex + i;
2355             tfid.cell = dscp->fid.cell;
2356             tfid.volume = bb.fids[j].Volume;
2357             tfid.vnode = bb.fids[j].Vnode;
2358             tfid.unique = bb.fids[j].Unique;
2359             code = cm_GetSCache(&tfid, &scp, userp, reqp);
2360             if (code != 0) 
2361                 continue;
2362
2363             /* otherwise, if this entry has no callback info, 
2364              * merge in this.
2365              */
2366             lock_ObtainMutex(&scp->mx);
2367             /* now, we have to be extra paranoid on merging in this
2368              * information, since we didn't use cm_SyncOp before
2369              * starting the fetch to make sure that no bad races
2370              * were occurring.  Specifically, we need to make sure
2371              * we don't obliterate any newer information in the
2372              * vnode than have here.
2373              *
2374              * Right now, be pretty conservative: if there's a
2375              * callback or a pending call, skip it.
2376              */
2377             if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2378                  && !(scp->flags &
2379                        (CM_SCACHEFLAG_FETCHING
2380                          | CM_SCACHEFLAG_STORING
2381                          | CM_SCACHEFLAG_SIZESTORING))) {
2382                 cm_EndCallbackGrantingCall(scp, &cbReq,
2383                                             &bb.callbacks[j],
2384                                             CM_CALLBACK_MAINTAINCOUNT);
2385                 cm_MergeStatus(dscp, scp, &bb.stats[j], &volSync, userp, 0);
2386             }       
2387             lock_ReleaseMutex(&scp->mx);
2388             cm_ReleaseSCache(scp);
2389         } /* all files in the response */
2390         /* now tell it to drop the count,
2391          * after doing the vnode processing above */
2392         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2393
2394         filex += filesThisCall;
2395     }   /* while there are still more files to process */
2396     lock_ObtainMutex(&dscp->mx);
2397
2398     /* If we did the InlineBulk RPC pull out the return code and log it */
2399     if (inlinebulk) {
2400         if ((&bb.stats[0])->errorCode) {
2401             osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d", 
2402                      (&bb.stats[0])->errorCode);
2403         }
2404     }
2405
2406     osi_Log0(afsd_logp, "END cm_TryBulkStat");
2407     return 0;
2408 }       
2409
2410 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2411 {
2412     long mask;
2413
2414     /* initialize store back mask as inexpensive local variable */
2415     mask = 0;
2416     memset(statusp, 0, sizeof(AFSStoreStatus));
2417
2418     /* copy out queued info from scache first, if scp passed in */
2419     if (scp) {
2420         if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2421             statusp->ClientModTime = scp->clientModTime;
2422             mask |= AFS_SETMODTIME;
2423             scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2424         }
2425     }
2426
2427     if (attrp) {
2428         /* now add in our locally generated request */
2429         if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2430             statusp->ClientModTime = attrp->clientModTime;
2431             mask |= AFS_SETMODTIME;
2432         }
2433         if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2434             statusp->UnixModeBits = attrp->unixModeBits;
2435             mask |= AFS_SETMODE;
2436         }
2437         if (attrp->mask & CM_ATTRMASK_OWNER) {
2438             statusp->Owner = attrp->owner;
2439             mask |= AFS_SETOWNER;
2440         }
2441         if (attrp->mask & CM_ATTRMASK_GROUP) {
2442             statusp->Group = attrp->group;
2443             mask |= AFS_SETGROUP;
2444         }
2445     }
2446     statusp->Mask = mask;
2447 }       
2448
2449 /* set the file size, and make sure that all relevant buffers have been
2450  * truncated.  Ensure that any partially truncated buffers have been zeroed
2451  * to the end of the buffer.
2452  */
2453 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2454                    cm_req_t *reqp)
2455 {
2456     long code;
2457     int shrinking;
2458
2459     /* start by locking out buffer creation */
2460     lock_ObtainWrite(&scp->bufCreateLock);
2461
2462     /* verify that this is a file, not a dir or a symlink */
2463     lock_ObtainMutex(&scp->mx);
2464     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2465                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2466     if (code) 
2467         goto done;
2468     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2469
2470     if (scp->fileType != CM_SCACHETYPE_FILE) {
2471         code = CM_ERROR_ISDIR;
2472         goto done;
2473     }
2474
2475   startover:
2476     if (LargeIntegerLessThan(*sizep, scp->length))
2477         shrinking = 1;
2478     else
2479         shrinking = 0;
2480
2481     lock_ReleaseMutex(&scp->mx);
2482
2483     /* can't hold scp->mx lock here, since we may wait for a storeback to
2484      * finish if the buffer package is cleaning a buffer by storing it to
2485      * the server.
2486      */
2487     if (shrinking)
2488         buf_Truncate(scp, userp, reqp, sizep);
2489
2490     /* now ensure that file length is short enough, and update truncPos */
2491     lock_ObtainMutex(&scp->mx);
2492
2493     /* make sure we have a callback (so we have the right value for the
2494      * length), and wait for it to be safe to do a truncate.
2495      */
2496     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2497                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2498                       | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2499
2500     /* If we only have 'i' bits, then we should still be able to set
2501        the size of a file we created. */
2502     if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2503         code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2504                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2505                          | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2506     }
2507
2508     if (code) 
2509         goto done;
2510
2511     if (LargeIntegerLessThan(*sizep, scp->length)) {
2512         /* a real truncation.  If truncPos is not set yet, or is bigger
2513          * than where we're truncating the file, set truncPos to this
2514          * new value.
2515          */
2516         if (!shrinking)
2517             goto startover;
2518         if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2519              || LargeIntegerLessThan(*sizep, scp->length)) {
2520             /* set trunc pos */
2521             scp->truncPos = *sizep;
2522             scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2523         }
2524         /* in either case, the new file size has been changed */
2525         scp->length = *sizep;
2526         scp->mask |= CM_SCACHEMASK_LENGTH;
2527     }
2528     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2529         /* really extending the file */
2530         scp->length = *sizep;
2531         scp->mask |= CM_SCACHEMASK_LENGTH;
2532     }
2533
2534     /* done successfully */
2535     code = 0;
2536
2537     cm_SyncOpDone(scp, NULL, 
2538                    CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2539                    | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2540
2541   done:
2542     lock_ReleaseMutex(&scp->mx);
2543     lock_ReleaseWrite(&scp->bufCreateLock);
2544
2545     return code;
2546 }
2547
2548 /* set the file size or other attributes (but not both at once) */
2549 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2550                 cm_req_t *reqp)
2551 {
2552     long code;
2553     AFSFetchStatus afsOutStatus;
2554     AFSVolSync volSync;
2555     cm_conn_t *connp;
2556     AFSFid tfid;
2557     AFSStoreStatus afsInStatus;
2558     struct rx_connection * callp;
2559
2560     /* handle file length setting */
2561     if (attrp->mask & CM_ATTRMASK_LENGTH)
2562         return cm_SetLength(scp, &attrp->length, userp, reqp);
2563
2564     lock_ObtainMutex(&scp->mx);
2565     /* otherwise, we have to make an RPC to get the status */
2566     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2567     if (code) {
2568         lock_ReleaseMutex(&scp->mx);
2569         return code;
2570     }
2571
2572     /* make the attr structure */
2573     cm_StatusFromAttr(&afsInStatus, scp, attrp);
2574
2575     tfid.Volume = scp->fid.volume;
2576     tfid.Vnode = scp->fid.vnode;
2577     tfid.Unique = scp->fid.unique;
2578         lock_ReleaseMutex(&scp->mx);
2579
2580     /* now make the RPC */
2581     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2582     do {
2583         code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2584         if (code) 
2585             continue;
2586
2587         callp = cm_GetRxConn(connp);
2588         code = RXAFS_StoreStatus(callp, &tfid,
2589                                   &afsInStatus, &afsOutStatus, &volSync);
2590         rx_PutConnection(callp);
2591
2592     } while (cm_Analyze(connp, userp, reqp,
2593                          &scp->fid, &volSync, NULL, NULL, code));
2594     code = cm_MapRPCError(code, reqp);
2595
2596     if (code)
2597         osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2598     else
2599         osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2600
2601     lock_ObtainMutex(&scp->mx);
2602     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2603     if (code == 0)
2604         cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
2605                         CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2606         
2607     /* if we're changing the mode bits, discard the ACL cache, 
2608      * since we changed the mode bits.
2609      */
2610     if (afsInStatus.Mask & AFS_SETMODE) 
2611         cm_FreeAllACLEnts(scp);
2612     lock_ReleaseMutex(&scp->mx);
2613     return code;
2614 }       
2615
2616 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2617                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2618 {       
2619     cm_conn_t *connp;
2620     long code;
2621     AFSFid dirAFSFid;
2622     cm_callbackRequest_t cbReq;
2623     AFSFid newAFSFid;
2624     cm_fid_t newFid;
2625     cm_scache_t *scp = NULL;
2626     int didEnd;
2627     AFSStoreStatus inStatus;
2628     AFSFetchStatus updatedDirStatus;
2629     AFSFetchStatus newFileStatus;
2630     AFSCallBack newFileCallback;
2631     AFSVolSync volSync;
2632     struct rx_connection * callp;
2633     cm_dirOp_t dirop;
2634
2635     /* can't create names with @sys in them; must expand it manually first.
2636      * return "invalid request" if they try.
2637      */
2638     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2639         return CM_ERROR_ATSYS;
2640     }
2641
2642     /* before starting the RPC, mark that we're changing the file data, so
2643      * that someone who does a chmod will know to wait until our call
2644      * completes.
2645      */
2646     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2647     lock_ObtainMutex(&dscp->mx);
2648     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2649     lock_ReleaseMutex(&dscp->mx);
2650     if (code == 0) {
2651         cm_StartCallbackGrantingCall(NULL, &cbReq);
2652     } else {
2653         cm_EndDirOp(&dirop);
2654     }
2655     if (code) {
2656         return code;
2657     }
2658     didEnd = 0;
2659
2660     cm_StatusFromAttr(&inStatus, NULL, attrp);
2661
2662     /* try the RPC now */
2663     osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2664     do {
2665         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2666         if (code) 
2667             continue;
2668
2669         dirAFSFid.Volume = dscp->fid.volume;
2670         dirAFSFid.Vnode = dscp->fid.vnode;
2671         dirAFSFid.Unique = dscp->fid.unique;
2672
2673         callp = cm_GetRxConn(connp);
2674         code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2675                                  &inStatus, &newAFSFid, &newFileStatus,
2676                                  &updatedDirStatus, &newFileCallback,
2677                                  &volSync);
2678         rx_PutConnection(callp);
2679
2680     } while (cm_Analyze(connp, userp, reqp,
2681                          &dscp->fid, &volSync, NULL, &cbReq, code));
2682     code = cm_MapRPCError(code, reqp);
2683         
2684     if (code)
2685         osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2686     else
2687         osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2688
2689     lock_ObtainWrite(&dscp->dirlock);
2690     dirop.lockType = CM_DIRLOCK_WRITE;
2691     lock_ObtainMutex(&dscp->mx);
2692     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2693     if (code == 0) {
2694         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
2695     }
2696     lock_ReleaseMutex(&dscp->mx);
2697
2698     /* now try to create the file's entry, too, but be careful to 
2699      * make sure that we don't merge in old info.  Since we weren't locking
2700      * out any requests during the file's creation, we may have pretty old
2701      * info.
2702      */
2703     if (code == 0) {
2704         newFid.cell = dscp->fid.cell;
2705         newFid.volume = dscp->fid.volume;
2706         newFid.vnode = newAFSFid.Vnode;
2707         newFid.unique = newAFSFid.Unique;
2708         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2709         if (code == 0) {
2710             lock_ObtainMutex(&scp->mx);
2711             scp->creator = userp;               /* remember who created it */
2712             if (!cm_HaveCallback(scp)) {
2713                 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2714                                 userp, 0);
2715                 cm_EndCallbackGrantingCall(scp, &cbReq,
2716                                             &newFileCallback, 0);
2717                 didEnd = 1;     
2718             }       
2719             lock_ReleaseMutex(&scp->mx);
2720             *scpp = scp;
2721         }
2722     }
2723
2724     /* make sure we end things properly */
2725     if (!didEnd)
2726         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2727
2728     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2729         cm_DirCreateEntry(&dirop, namep, &newFid);
2730 #ifdef USE_BPLUS
2731         cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
2732 #endif
2733     }
2734     cm_EndDirOp(&dirop);
2735
2736     return code;
2737 }       
2738
2739 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2740 {
2741     long code;
2742
2743     lock_ObtainWrite(&scp->bufCreateLock);
2744     code = buf_CleanVnode(scp, userp, reqp);
2745     lock_ReleaseWrite(&scp->bufCreateLock);
2746     if (code == 0) {
2747         lock_ObtainMutex(&scp->mx);
2748
2749         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2750                           | CM_SCACHEMASK_CLIENTMODTIME
2751                           | CM_SCACHEMASK_LENGTH))
2752             code = cm_StoreMini(scp, userp, reqp);
2753
2754         if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2755             code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2756             scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2757         }
2758
2759         lock_ReleaseMutex(&scp->mx);
2760     }
2761     return code;
2762 }
2763
2764 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2765                  cm_user_t *userp, cm_req_t *reqp)
2766 {
2767     cm_conn_t *connp;
2768     long code;
2769     AFSFid dirAFSFid;
2770     cm_callbackRequest_t cbReq;
2771     AFSFid newAFSFid;
2772     cm_fid_t newFid;
2773     cm_scache_t *scp = NULL;
2774     int didEnd;
2775     AFSStoreStatus inStatus;
2776     AFSFetchStatus updatedDirStatus;
2777     AFSFetchStatus newDirStatus;
2778     AFSCallBack newDirCallback;
2779     AFSVolSync volSync;
2780     struct rx_connection * callp;
2781     cm_dirOp_t dirop;
2782
2783     /* can't create names with @sys in them; must expand it manually first.
2784      * return "invalid request" if they try.
2785      */
2786     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2787         return CM_ERROR_ATSYS;
2788     }
2789
2790     /* before starting the RPC, mark that we're changing the directory
2791      * data, so that someone who does a chmod on the dir will wait until
2792      * our call completes.
2793      */
2794     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2795     lock_ObtainMutex(&dscp->mx);
2796     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2797     lock_ReleaseMutex(&dscp->mx);
2798     if (code == 0) {
2799         cm_StartCallbackGrantingCall(NULL, &cbReq);
2800     } else {
2801         cm_EndDirOp(&dirop);
2802     }
2803     if (code) {
2804         return code;
2805     }
2806     didEnd = 0;
2807
2808     cm_StatusFromAttr(&inStatus, NULL, attrp);
2809
2810     /* try the RPC now */
2811     osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2812     do {
2813         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2814         if (code) 
2815             continue;
2816
2817         dirAFSFid.Volume = dscp->fid.volume;
2818         dirAFSFid.Vnode = dscp->fid.vnode;
2819         dirAFSFid.Unique = dscp->fid.unique;
2820
2821         callp = cm_GetRxConn(connp);
2822         code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2823                               &inStatus, &newAFSFid, &newDirStatus,
2824                               &updatedDirStatus, &newDirCallback,
2825                               &volSync);
2826         rx_PutConnection(callp);
2827
2828     } while (cm_Analyze(connp, userp, reqp,
2829                          &dscp->fid, &volSync, NULL, &cbReq, code));
2830     code = cm_MapRPCError(code, reqp);
2831         
2832     if (code)
2833         osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2834     else
2835         osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2836
2837     lock_ObtainWrite(&dscp->dirlock);
2838     dirop.lockType = CM_DIRLOCK_WRITE;
2839     lock_ObtainMutex(&dscp->mx);
2840     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2841     if (code == 0) {
2842         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
2843     }
2844     lock_ReleaseMutex(&dscp->mx);
2845
2846     /* now try to create the new dir's entry, too, but be careful to 
2847      * make sure that we don't merge in old info.  Since we weren't locking
2848      * out any requests during the file's creation, we may have pretty old
2849      * info.
2850      */
2851     if (code == 0) {
2852         newFid.cell = dscp->fid.cell;
2853         newFid.volume = dscp->fid.volume;
2854         newFid.vnode = newAFSFid.Vnode;
2855         newFid.unique = newAFSFid.Unique;
2856         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2857         if (code == 0) {
2858             lock_ObtainMutex(&scp->mx);
2859             if (!cm_HaveCallback(scp)) {
2860                 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2861                                 userp, 0);
2862                 cm_EndCallbackGrantingCall(scp, &cbReq,
2863                                             &newDirCallback, 0);
2864                 didEnd = 1;             
2865             }
2866             lock_ReleaseMutex(&scp->mx);
2867             cm_ReleaseSCache(scp);
2868         }
2869     }
2870
2871     /* make sure we end things properly */
2872     if (!didEnd)
2873         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2874
2875     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2876         cm_DirCreateEntry(&dirop, namep, &newFid);
2877 #ifdef USE_BPLUS
2878         cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
2879 #endif
2880     }
2881     cm_EndDirOp(&dirop);
2882
2883     /* and return error code */
2884     return code;
2885 }       
2886
2887 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2888              cm_user_t *userp, cm_req_t *reqp)
2889 {
2890     cm_conn_t *connp;
2891     long code = 0;
2892     AFSFid dirAFSFid;
2893     AFSFid existingAFSFid;
2894     AFSFetchStatus updatedDirStatus;
2895     AFSFetchStatus newLinkStatus;
2896     AFSVolSync volSync;
2897     struct rx_connection * callp;
2898     cm_dirOp_t dirop;
2899
2900     if (dscp->fid.cell != sscp->fid.cell ||
2901         dscp->fid.volume != sscp->fid.volume) {
2902         return CM_ERROR_CROSSDEVLINK;
2903     }
2904
2905     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2906     lock_ObtainMutex(&dscp->mx);
2907     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2908     lock_ReleaseMutex(&dscp->mx);
2909     if (code != 0)
2910         cm_EndDirOp(&dirop);
2911
2912     if (code)
2913         return code;
2914
2915     /* try the RPC now */
2916     osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2917     do {
2918         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2919         if (code) continue;
2920
2921         dirAFSFid.Volume = dscp->fid.volume;
2922         dirAFSFid.Vnode = dscp->fid.vnode;
2923         dirAFSFid.Unique = dscp->fid.unique;
2924
2925         existingAFSFid.Volume = sscp->fid.volume;
2926         existingAFSFid.Vnode = sscp->fid.vnode;
2927         existingAFSFid.Unique = sscp->fid.unique;
2928
2929         callp = cm_GetRxConn(connp);
2930         code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2931             &newLinkStatus, &updatedDirStatus, &volSync);
2932         rx_PutConnection(callp);
2933         osi_Log1(smb_logp,"  RXAFS_Link returns 0x%x", code);
2934
2935     } while (cm_Analyze(connp, userp, reqp,
2936         &dscp->fid, &volSync, NULL, NULL, code));
2937
2938     code = cm_MapRPCError(code, reqp);
2939
2940     if (code)
2941         osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2942     else
2943         osi_Log0(afsd_logp, "CALL Link SUCCESS");
2944
2945     lock_ObtainWrite(&dscp->dirlock);
2946     dirop.lockType = CM_DIRLOCK_WRITE;
2947     lock_ObtainMutex(&dscp->mx);
2948     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2949     if (code == 0) {
2950         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
2951         if (cm_CheckDirOpForSingleChange(&dirop)) {
2952             cm_DirCreateEntry(&dirop, namep, &sscp->fid);
2953 #ifdef USE_BPLUS
2954             cm_BPlusDirCreateEntry(&dirop, namep, &sscp->fid);
2955 #endif
2956         }
2957     }
2958     cm_EndDirOp(&dirop);
2959     lock_ReleaseMutex(&dscp->mx);
2960
2961     return code;
2962 }
2963
2964 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2965                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2966 {
2967     cm_conn_t *connp;
2968     long code;
2969     AFSFid dirAFSFid;
2970     AFSFid newAFSFid;
2971     cm_fid_t newFid;
2972     cm_scache_t *scp;
2973     AFSStoreStatus inStatus;
2974     AFSFetchStatus updatedDirStatus;
2975     AFSFetchStatus newLinkStatus;
2976     AFSVolSync volSync;
2977     struct rx_connection * callp;
2978     cm_dirOp_t dirop;
2979
2980     /* before starting the RPC, mark that we're changing the directory data,
2981      * so that someone who does a chmod on the dir will wait until our
2982      * call completes.
2983      */
2984     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2985     lock_ObtainMutex(&dscp->mx);
2986     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2987     lock_ReleaseMutex(&dscp->mx);
2988     if (code != 0)
2989         cm_EndDirOp(&dirop);
2990     if (code) {
2991         return code;
2992     }
2993
2994     cm_StatusFromAttr(&inStatus, NULL, attrp);
2995
2996     /* try the RPC now */
2997     osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
2998     do {
2999         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3000         if (code) 
3001             continue;
3002
3003         dirAFSFid.Volume = dscp->fid.volume;
3004         dirAFSFid.Vnode = dscp->fid.vnode;
3005         dirAFSFid.Unique = dscp->fid.unique;
3006
3007         callp = cm_GetRxConn(connp);
3008         code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
3009                               &inStatus, &newAFSFid, &newLinkStatus,
3010                               &updatedDirStatus, &volSync);
3011         rx_PutConnection(callp);
3012
3013     } while (cm_Analyze(connp, userp, reqp,
3014                          &dscp->fid, &volSync, NULL, NULL, code));
3015     code = cm_MapRPCError(code, reqp);
3016         
3017     if (code)
3018         osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3019     else
3020         osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3021
3022     lock_ObtainWrite(&dscp->dirlock);
3023     dirop.lockType = CM_DIRLOCK_WRITE;
3024     lock_ObtainMutex(&dscp->mx);
3025     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3026     if (code == 0) {
3027         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
3028         if (cm_CheckDirOpForSingleChange(&dirop)) {
3029             newFid.cell = dscp->fid.cell;
3030             newFid.volume = dscp->fid.volume;
3031             newFid.vnode = newAFSFid.Vnode;
3032             newFid.unique = newAFSFid.Unique;
3033
3034             cm_DirCreateEntry(&dirop, namep, &newFid);
3035 #ifdef USE_BPLUS
3036             cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
3037 #endif
3038         }
3039     }
3040     cm_EndDirOp(&dirop);
3041     lock_ReleaseMutex(&dscp->mx);
3042
3043     /* now try to create the new dir's entry, too, but be careful to 
3044      * make sure that we don't merge in old info.  Since we weren't locking
3045      * out any requests during the file's creation, we may have pretty old
3046      * info.
3047      */
3048     if (code == 0) {
3049         newFid.cell = dscp->fid.cell;
3050         newFid.volume = dscp->fid.volume;
3051         newFid.vnode = newAFSFid.Vnode;
3052         newFid.unique = newAFSFid.Unique;
3053         code = cm_GetSCache(&newFid, &scp, userp, reqp);
3054         if (code == 0) {
3055             lock_ObtainMutex(&scp->mx);
3056             if (!cm_HaveCallback(scp)) {
3057                 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3058                                 userp, 0);
3059             }       
3060             lock_ReleaseMutex(&scp->mx);
3061             cm_ReleaseSCache(scp);
3062         }
3063     }
3064         
3065     /* and return error code */
3066     return code;
3067 }
3068
3069 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
3070                    cm_req_t *reqp)
3071 {
3072     cm_conn_t *connp;
3073     long code;
3074     AFSFid dirAFSFid;
3075     int didEnd;
3076     AFSFetchStatus updatedDirStatus;
3077     AFSVolSync volSync;
3078     struct rx_connection * callp;
3079     cm_dirOp_t dirop;
3080
3081     /* before starting the RPC, mark that we're changing the directory data,
3082      * so that someone who does a chmod on the dir will wait until our
3083      * call completes.
3084      */
3085     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3086     lock_ObtainMutex(&dscp->mx);
3087     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3088     lock_ReleaseMutex(&dscp->mx);
3089     if (code) {
3090         cm_EndDirOp(&dirop);
3091         return code;
3092     }
3093     didEnd = 0;
3094
3095     /* try the RPC now */
3096     osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3097     do {
3098         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3099         if (code) 
3100             continue;
3101
3102         dirAFSFid.Volume = dscp->fid.volume;
3103         dirAFSFid.Vnode = dscp->fid.vnode;
3104         dirAFSFid.Unique = dscp->fid.unique;
3105
3106         callp = cm_GetRxConn(connp);
3107         code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
3108                                 &updatedDirStatus, &volSync);
3109         rx_PutConnection(callp);
3110
3111     } while (cm_Analyze(connp, userp, reqp,
3112                          &dscp->fid, &volSync, NULL, NULL, code));
3113     code = cm_MapRPCErrorRmdir(code, reqp);
3114
3115     if (code)
3116         osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3117     else
3118         osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3119
3120     lock_ObtainWrite(&dscp->dirlock);
3121     dirop.lockType = CM_DIRLOCK_WRITE;
3122     lock_ObtainMutex(&dscp->mx);
3123     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3124     if (code == 0) {
3125         cm_dnlcRemove(dscp, namep); 
3126         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
3127     }
3128     lock_ReleaseMutex(&dscp->mx);
3129
3130     if (code == 0) {
3131         if (cm_CheckDirOpForSingleChange(&dirop)) {
3132             cm_DirDeleteEntry(&dirop, namep);
3133 #ifdef USE_BPLUS
3134             cm_BPlusDirDeleteEntry(&dirop, namep);
3135 #endif
3136         }
3137     }
3138     cm_EndDirOp(&dirop);
3139
3140     /* and return error code */
3141     return code;
3142 }
3143
3144 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3145 {
3146     /* grab mutex on contents */
3147     lock_ObtainMutex(&scp->mx);
3148
3149     /* reset the prefetch info */
3150     scp->prefetch.base.LowPart = 0;             /* base */
3151     scp->prefetch.base.HighPart = 0;
3152     scp->prefetch.end.LowPart = 0;              /* and end */
3153     scp->prefetch.end.HighPart = 0;
3154
3155     /* release mutex on contents */
3156     lock_ReleaseMutex(&scp->mx);
3157
3158     /* we're done */
3159     return 0;
3160 }       
3161
3162 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
3163                 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
3164 {
3165     cm_conn_t *connp;
3166     long code;
3167     AFSFid oldDirAFSFid;
3168     AFSFid newDirAFSFid;
3169     int didEnd;
3170     AFSFetchStatus updatedOldDirStatus;
3171     AFSFetchStatus updatedNewDirStatus;
3172     AFSVolSync volSync;
3173     int oneDir;
3174     struct rx_connection * callp;
3175     cm_dirOp_t oldDirOp;
3176     cm_fid_t   fileFid;
3177     int        diropCode = -1;
3178     cm_dirOp_t newDirOp;
3179
3180     /* before starting the RPC, mark that we're changing the directory data,
3181      * so that someone who does a chmod on the dir will wait until our call
3182      * completes.  We do this in vnode order so that we don't deadlock,
3183      * which makes the code a little verbose.
3184      */
3185     if (oldDscp == newDscp) {
3186         /* check for identical names */
3187         if (strcmp(oldNamep, newNamep) == 0)
3188             return CM_ERROR_RENAME_IDENTICAL;
3189
3190         oneDir = 1;
3191         cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3192         lock_ObtainMutex(&oldDscp->mx);
3193         cm_dnlcRemove(oldDscp, oldNamep);
3194         cm_dnlcRemove(oldDscp, newNamep);
3195         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3196                           CM_SCACHESYNC_STOREDATA);
3197         lock_ReleaseMutex(&oldDscp->mx);
3198         if (code != 0) {
3199             cm_EndDirOp(&oldDirOp);
3200         }
3201     }
3202     else {
3203         /* two distinct dir vnodes */
3204         oneDir = 0;
3205         if (oldDscp->fid.cell != newDscp->fid.cell ||
3206              oldDscp->fid.volume != newDscp->fid.volume)
3207             return CM_ERROR_CROSSDEVLINK;
3208
3209         /* shouldn't happen that we have distinct vnodes for two
3210          * different files, but could due to deliberate attack, or
3211          * stale info.  Avoid deadlocks and quit now.
3212          */
3213         if (oldDscp->fid.vnode == newDscp->fid.vnode)
3214             return CM_ERROR_CROSSDEVLINK;
3215
3216         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3217             cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3218             lock_ObtainMutex(&oldDscp->mx);
3219             cm_dnlcRemove(oldDscp, oldNamep);
3220             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3221                               CM_SCACHESYNC_STOREDATA);
3222             lock_ReleaseMutex(&oldDscp->mx);
3223             if (code != 0)
3224                 cm_EndDirOp(&oldDirOp);
3225             if (code == 0) {
3226                 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3227                 lock_ObtainMutex(&newDscp->mx);
3228                 cm_dnlcRemove(newDscp, newNamep);
3229                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3230                                   CM_SCACHESYNC_STOREDATA);
3231                 lock_ReleaseMutex(&newDscp->mx);
3232                 if (code) {
3233                     cm_EndDirOp(&newDirOp);
3234
3235                     /* cleanup first one */
3236                     lock_ObtainMutex(&oldDscp->mx);
3237                     cm_SyncOpDone(oldDscp, NULL,
3238                                    CM_SCACHESYNC_STOREDATA);
3239                     lock_ReleaseMutex(&oldDscp->mx);
3240                     cm_EndDirOp(&oldDirOp);
3241                 }       
3242             }
3243         }
3244         else {
3245             /* lock the new vnode entry first */
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 != 0)
3253                 cm_EndDirOp(&newDirOp);
3254             if (code == 0) {
3255                 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3256                 lock_ObtainMutex(&oldDscp->mx);
3257                 cm_dnlcRemove(oldDscp, oldNamep);
3258                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3259                                   CM_SCACHESYNC_STOREDATA);
3260                 lock_ReleaseMutex(&oldDscp->mx);
3261                 if (code != 0)
3262                     cm_EndDirOp(&oldDirOp);
3263                 if (code) {
3264                     /* cleanup first one */
3265                     lock_ObtainMutex(&newDscp->mx);
3266                     cm_SyncOpDone(newDscp, NULL,
3267                                    CM_SCACHESYNC_STOREDATA);
3268                     lock_ReleaseMutex(&newDscp->mx);
3269                     cm_EndDirOp(&newDirOp);
3270                 }       
3271             }
3272         }
3273     }   /* two distinct vnodes */
3274
3275     if (code) {
3276         return code;
3277     }
3278     didEnd = 0;
3279
3280     /* try the RPC now */
3281     osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p", 
3282               oldDscp, newDscp);
3283     do {
3284         code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3285         if (code) 
3286             continue;
3287
3288         oldDirAFSFid.Volume = oldDscp->fid.volume;
3289         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3290         oldDirAFSFid.Unique = oldDscp->fid.unique;
3291         newDirAFSFid.Volume = newDscp->fid.volume;
3292         newDirAFSFid.Vnode = newDscp->fid.vnode;
3293         newDirAFSFid.Unique = newDscp->fid.unique;
3294
3295         callp = cm_GetRxConn(connp);
3296         code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
3297                              &newDirAFSFid, newNamep,
3298                              &updatedOldDirStatus, &updatedNewDirStatus,
3299                              &volSync);
3300         rx_PutConnection(callp);
3301
3302     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3303                          &volSync, NULL, NULL, code));
3304     code = cm_MapRPCError(code, reqp);
3305         
3306     if (code)
3307         osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3308     else
3309         osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3310
3311     /* update the individual stat cache entries for the directories */
3312     lock_ObtainWrite(&oldDscp->dirlock);
3313     oldDirOp.lockType = CM_DIRLOCK_WRITE;
3314     lock_ObtainMutex(&oldDscp->mx);
3315     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3316
3317     if (code == 0)
3318         cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3319                         userp, 0);
3320     lock_ReleaseMutex(&oldDscp->mx);
3321
3322     if (code == 0) {
3323         if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
3324
3325 #ifdef USE_BPLUS
3326             diropCode = cm_BPlusDirLookup(&oldDirOp, oldNamep, &fileFid);
3327             if (diropCode == CM_ERROR_INEXACT_MATCH)
3328                 diropCode = 0;
3329             else if (diropCode == EINVAL)
3330 #endif
3331                 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3332
3333             if (diropCode == 0) {
3334                 if (oneDir) {
3335                     diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3336 #ifdef USE_BPLUS
3337                     cm_BPlusDirCreateEntry(&oldDirOp, newNamep, &fileFid);
3338 #endif
3339                 }
3340
3341                 if (diropCode == 0) { 
3342                     diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3343 #ifdef USE_BPLUS
3344                     cm_BPlusDirDeleteEntry(&oldDirOp, oldNamep);
3345 #endif
3346                 }
3347             }
3348         }
3349     }
3350     cm_EndDirOp(&oldDirOp);
3351
3352     /* and update it for the new one, too, if necessary */
3353     if (!oneDir) {
3354         lock_ObtainWrite(&newDscp->dirlock);
3355         newDirOp.lockType = CM_DIRLOCK_WRITE;
3356         lock_ObtainMutex(&newDscp->mx);
3357         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3358         if (code == 0)
3359             cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3360                             userp, 0);
3361         lock_ReleaseMutex(&newDscp->mx);
3362
3363         if (code == 0) {
3364             /* we only make the local change if we successfully made
3365                the change in the old directory AND there was only one
3366                change in the new directory */
3367             if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3368                 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3369 #ifdef USE_BPLUS
3370                 cm_BPlusDirCreateEntry(&newDirOp, newNamep, &fileFid);
3371 #endif
3372             }
3373         }
3374         cm_EndDirOp(&newDirOp);
3375     }
3376
3377     /* and return error code */
3378     return code;
3379 }
3380
3381 /* Byte range locks:
3382
3383    The OpenAFS Windows client has to fake byte range locks given no
3384    server side support for such locks.  This is implemented as keyed
3385    byte range locks on the cache manager.
3386
3387    Keyed byte range locks:
3388
3389    Each cm_scache_t structure keeps track of a list of keyed locks.
3390    The key for a lock identifies an owner of a set of locks (referred
3391    to as a client).  Each key is represented by a value.  The set of
3392    key values used within a specific cm_scache_t structure form a
3393    namespace that has a scope of just that cm_scache_t structure.  The
3394    same key value can be used with another cm_scache_t structure and
3395    correspond to a completely different client.  However it is
3396    advantageous for the SMB or IFS layer to make sure that there is a
3397    1-1 mapping between client and keys over all cm_scache_t objects.
3398
3399    Assume a client C has key Key(C) (although, since the scope of the
3400    key is a cm_scache_t, the key can be Key(C,S), where S is the
3401    cm_scache_t.  But assume a 1-1 relation between keys and clients).
3402    A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3403    inclusive (a.k.a. [O,O+L-1]).  The function Key(x) is implemented
3404    through cm_generateKey() function for both SMB and IFS.
3405
3406    The list of locks for a cm_scache_t object S is maintained in
3407    S->fileLocks.  The cache manager will set a lock on the AFS file
3408    server in order to assert the locks in S->fileLocks.  If only
3409    shared locks are in place for S, then the cache manager will obtain
3410    a LockRead lock, while if there are any exclusive locks, it will
3411    obtain a LockWrite lock.  If the exclusive locks are all released
3412    while the shared locks remain, then the cache manager will
3413    downgrade the lock from LockWrite to LockRead.  Similarly, if an
3414    exclusive lock is obtained when only shared locks exist, then the
3415    cache manager will try to upgrade the lock from LockRead to
3416    LockWrite.
3417
3418    Each lock L owned by client C maintains a key L->key such that
3419    L->key == Key(C), the effective range defined by L->LOffset and
3420    L->LLength such that the range of bytes affected by the lock is
3421    (L->LOffset, +L->LLength), a type maintained in L->LockType which
3422    is either exclusive or shared.
3423
3424    Lock states:
3425
3426    A lock exists iff it is in S->fileLocks for some cm_scache_t
3427    S. Existing locks are in one of the following states: ACTIVE,
3428    WAITLOCK, WAITUNLOCK, LOST, DELETED.
3429
3430    The following sections describe each lock and the associated
3431    transitions.
3432
3433    1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3434       the lock with the AFS file server.  This type of lock can be
3435       exercised by a client to read or write to the locked region (as
3436       the lock allows).
3437
3438       1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3439         server lock that was required to assert the lock.  Before
3440         marking the lock as lost, the cache manager checks if the file
3441         has changed on the server.  If the file has not changed, then
3442         the cache manager will attempt to obtain a new server lock
3443         that is sufficient to assert the client side locks for the
3444         file.  If any of these fail, the lock is marked as LOST.
3445         Otherwise, it is left as ACTIVE.
3446
3447       1.2 ACTIVE->DELETED: Lock is released.
3448
3449    2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3450       grants the lock but the lock is yet to be asserted with the AFS
3451       file server.  Once the file server grants the lock, the state
3452       will transition to an ACTIVE lock.
3453
3454       2.1 WAITLOCK->ACTIVE: The server granted the lock.
3455
3456       2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3457         waiting.
3458
3459       2.3 WAITLOCK->LOST: One or more locks from this client were
3460         marked as LOST.  No further locks will be granted to this
3461         client until all lost locks are removed.
3462
3463    3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3464       receives a request for a lock that conflicts with an existing
3465       ACTIVE or WAITLOCK lock.  The lock will be placed in the queue
3466       and will be granted at such time the conflicting locks are
3467       removed, at which point the state will transition to either
3468       WAITLOCK or ACTIVE.
3469
3470       3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed.  The
3471         current serverLock is sufficient to assert this lock, or a
3472         sufficient serverLock is obtained.
3473
3474       3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3475         however the required serverLock is yet to be asserted with the
3476         server.
3477
3478       3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3479         released.
3480
3481       3.5 WAITUNLOCK->LOST: One or more locks from this client were
3482         marked as LOST.  No further locks will be granted to this
3483         client until all lost locks are removed.
3484
3485    4. LOST: A lock L is LOST if the server lock that was required to
3486       assert the lock could not be obtained or if it could not be
3487       extended, or if other locks by the same client were LOST.
3488       Essentially, once a lock is LOST, the contract between the cache
3489       manager and that specific client is no longer valid.
3490
3491       The cache manager rechecks the server lock once every minute and
3492       extends it as appropriate.  If this is not done for 5 minutes,
3493       the AFS file server will release the lock (the 5 minute timeout
3494       is based on current file server code and is fairly arbitrary).
3495       Once released, the lock cannot be re-obtained without verifying
3496       that the contents of the file hasn't been modified since the
3497       time the lock was released.  Re-obtaining the lock without
3498       verifying this may lead to data corruption.  If the lock can not
3499       be obtained safely, then all active locks for the cm_scache_t
3500       are marked as LOST.
3501
3502       4.1 LOST->DELETED: The lock is released.
3503
3504    5. DELETED: The lock is no longer relevant.  Eventually, it will
3505       get removed from the cm_scache_t. In the meantime, it will be
3506       treated as if it does not exist.
3507
3508       5.1 DELETED->not exist: The lock is removed from the
3509         cm_scache_t.
3510
3511    The following are classifications of locks based on their state.
3512
3513    6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK.  These locks
3514       have been accepted by the cache manager, but may or may not have
3515       been granted back to the client.
3516
3517    7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3518
3519    8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3520
3521    Lock operation:
3522
3523    A client C can READ range (Offset,+Length) of a file represented by
3524    cm_scache_t S iff (1):
3525
3526    1. for all _a_ in (Offset,+Length), all of the following is true:
3527
3528        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3529          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3530          shared.
3531
3532        1.2 For each LOST lock L in S->fileLocks such that _a_ in
3533          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3534          Key(C)
3535
3536        (When locks are lost on an cm_scache_t, all locks are lost.  By
3537        4.2 (below), if there is an exclusive LOST lock, then there
3538        can't be any overlapping ACTIVE locks.)
3539
3540    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3541
3542    2. for all _a_ in (Offset,+Length), one of the following is true:
3543
3544        2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3545          does not exist a LOST lock L such that _a_ in
3546          (L->LOffset,+L->LLength).
3547
3548        2.2 Byte _a_ of S is owned by C under lock L (as specified in
3549          1.2) AND L->LockType is exclusive.
3550
3551    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3552
3553    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3554       true:
3555
3556        3.1 If L->LockType is exclusive then there does NOT exist a
3557          ACCEPTED lock M in S->fileLocks such that _a_ in
3558          (M->LOffset,+M->LLength).
3559
3560          (If we count all QUEUED locks then we hit cases such as
3561          cascading waiting locks where the locks later on in the queue
3562          can be granted without compromising file integrity.  On the
3563          other hand if only ACCEPTED locks are considered, then locks
3564          that were received earlier may end up waiting for locks that
3565          were received later to be unlocked. The choice of ACCEPTED
3566          locks was made to mimic the Windows byte range lock
3567          semantics.)
3568
3569        3.2 If L->LockType is shared then for each ACCEPTED lock M in
3570          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3571          M->LockType is shared.
3572
3573    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3574
3575        4.1 M->key != Key(C)
3576
3577        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3578          and (M->LOffset,+M->LLength) do not intersect.
3579
3580          (Note: If a client loses a lock, it loses all locks.
3581          Subsequently, it will not be allowed to obtain any more locks
3582          until all existing LOST locks that belong to the client are
3583          released.  Once all locks are released by a single client,
3584          there exists no further contract between the client and AFS
3585          about the contents of the file, hence the client can then
3586          proceed to obtain new locks and establish a new contract.
3587
3588          This doesn't quite work as you think it should, because most
3589          applications aren't built to deal with losing locks they
3590          thought they once had.  For now, we don't have a good
3591          solution to lost locks.
3592
3593          Also, for consistency reasons, we have to hold off on
3594          granting locks that overlap exclusive LOST locks.)
3595
3596    A client C can only unlock locks L in S->fileLocks which have
3597    L->key == Key(C).
3598
3599    The representation and invariants are as follows:
3600
3601    - Each cm_scache_t structure keeps:
3602
3603        - A queue of byte-range locks (cm_scache_t::fileLocks) which
3604          are of type cm_file_lock_t.
3605
3606        - A record of the highest server-side lock that has been
3607          obtained for this object (cm_scache_t::serverLock), which is
3608          one of (-1), LockRead, LockWrite.
3609
3610        - A count of ACCEPTED exclusive and shared locks that are in the
3611          queue (cm_scache_t::sharedLocks and
3612          cm_scache_t::exclusiveLocks)
3613
3614    - Each cm_file_lock_t structure keeps:
3615
3616        - The type of lock (cm_file_lock_t::LockType)
3617
3618        - The key associated with the lock (cm_file_lock_t::key)
3619
3620        - The offset and length of the lock (cm_file_lock_t::LOffset
3621          and cm_file_lock_t::LLength)
3622
3623        - The state of the lock.
3624
3625        - Time of issuance or last successful extension
3626
3627    Semantic invariants:
3628
3629        I1. The number of ACCEPTED locks in S->fileLocks are
3630            (S->sharedLocks + S->exclusiveLocks)
3631
3632    External invariants:
3633
3634        I3. S->serverLock is the lock that we have asserted with the
3635            AFS file server for this cm_scache_t.
3636
3637        I4. S->serverLock == LockRead iff there is at least one ACTIVE
3638            shared lock, but no ACTIVE exclusive locks.
3639
3640        I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3641            exclusive lock.
3642
3643        I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3644            M->key == L->key IMPLIES M is LOST or DELETED.
3645
3646    --asanka
3647  */
3648
3649 #define IS_LOCK_ACTIVE(lockp)     (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3650
3651 #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)
3652
3653 #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)
3654
3655 #define IS_LOCK_LOST(lockp)       (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3656
3657 #define IS_LOCK_DELETED(lockp)    (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3658
3659 /* unsafe */
3660 #define IS_LOCK_ACCEPTED(lockp)   (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3661
3662 /* unsafe */
3663 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3664
3665 /* unsafe */
3666 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3667
3668 /* unsafe */
3669 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3670
3671 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3672 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3673 #else
3674 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3675 #endif
3676
3677 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3678
3679 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3680 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3681 #else
3682 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3683
3684 /* This should really be defined in any build that this code is being
3685    compiled. */
3686 #error  VICED_CAPABILITY_WRITELOCKACL not defined.
3687 #endif
3688
3689 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3690 {
3691     afs_int64 int_begin;
3692     afs_int64 int_end;
3693
3694     int_begin = MAX(pos->offset, neg->offset);
3695     int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3696
3697     if (int_begin < int_end) {
3698         if (int_begin == pos->offset) {
3699             pos->length = pos->offset + pos->length - int_end;
3700             pos->offset = int_end;
3701         } else if (int_end == pos->offset + pos->length) {
3702             pos->length = int_begin - pos->offset;
3703         }
3704
3705         /* We only subtract ranges if the resulting range is
3706            contiguous.  If we try to support non-contigous ranges, we
3707            aren't actually improving performance. */
3708     }
3709 }
3710
3711 /* Called with scp->mx held.  Returns 0 if all is clear to read the
3712    specified range by the client identified by key.
3713  */
3714 long cm_LockCheckRead(cm_scache_t *scp, 
3715                       LARGE_INTEGER LOffset, 
3716                       LARGE_INTEGER LLength, 
3717                       cm_key_t key)
3718 {
3719 #ifndef ADVISORY_LOCKS
3720
3721     cm_file_lock_t *fileLock;
3722     osi_queue_t *q;
3723     long code = 0;
3724     cm_range_t range;
3725     int substract_ranges = FALSE;
3726
3727     range.offset = LOffset.QuadPart;
3728     range.length = LLength.QuadPart;
3729
3730     /*
3731
3732      1. for all _a_ in (Offset,+Length), all of the following is true:
3733
3734        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3735          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3736          shared.
3737
3738        1.2 For each LOST lock L in S->fileLocks such that _a_ in
3739          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3740          Key(C)
3741
3742     */
3743
3744     lock_ObtainRead(&cm_scacheLock);
3745
3746     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3747         fileLock = 
3748             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3749
3750         if (INTERSECT_RANGE(range, fileLock->range)) {
3751             if (IS_LOCK_ACTIVE(fileLock)) {
3752                 if (fileLock->key == key) {
3753
3754                     /* If there is an active lock for this client, it
3755                        is safe to substract ranges.*/
3756                     cm_LockRangeSubtract(&range, &fileLock->range);
3757                     substract_ranges = TRUE;
3758                 } else {
3759                     if (fileLock->lockType != LockRead) {
3760                         code = CM_ERROR_LOCK_CONFLICT;
3761                         break;
3762                     }
3763
3764                     /* even if the entire range is locked for reading,
3765                        we still can't grant the lock at this point
3766                        because the client may have lost locks. That
3767                        is, unless we have already seen an active lock
3768                        belonging to the client, in which case there
3769                        can't be any lost locks for this client. */
3770                     if (substract_ranges)
3771                         cm_LockRangeSubtract(&range, &fileLock->range);
3772                 }
3773             } else if (IS_LOCK_LOST(fileLock) &&
3774                       (fileLock->key == key || fileLock->lockType == LockWrite)) {
3775                 code = CM_ERROR_BADFD;
3776                 break;
3777             }
3778         }
3779     }
3780
3781     lock_ReleaseRead(&cm_scacheLock);
3782
3783     osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3784               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3785
3786     return code;
3787
3788 #else
3789
3790     return 0;
3791
3792 #endif
3793 }
3794
3795 /* Called with scp->mx held.  Returns 0 if all is clear to write the
3796    specified range by the client identified by key.
3797  */
3798 long cm_LockCheckWrite(cm_scache_t *scp,
3799                        LARGE_INTEGER LOffset,
3800                        LARGE_INTEGER LLength,
3801                        cm_key_t key)
3802 {
3803 #ifndef ADVISORY_LOCKS
3804
3805     cm_file_lock_t *fileLock;
3806     osi_queue_t *q;
3807     long code = 0;
3808     cm_range_t range;
3809
3810     range.offset = LOffset.QuadPart;
3811     range.length = LLength.QuadPart;
3812
3813     /*
3814    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3815
3816    2. for all _a_ in (Offset,+Length), one of the following is true:
3817
3818        2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3819          lock L such that _a_ in (L->LOffset,+L->LLength).
3820
3821        2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3822          exclusive.
3823     */
3824
3825     lock_ObtainRead(&cm_scacheLock);
3826
3827     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3828         fileLock = 
3829             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3830
3831         if (INTERSECT_RANGE(range, fileLock->range)) {
3832             if (IS_LOCK_ACTIVE(fileLock)) {
3833                 if (fileLock->key == key) {
3834                     if (fileLock->lockType == LockWrite) {
3835
3836                         /* if there is an active lock for this client, it
3837                            is safe to substract ranges */
3838                         cm_LockRangeSubtract(&range, &fileLock->range);
3839                     } else {
3840                         code = CM_ERROR_LOCK_CONFLICT;
3841                         break;
3842                     }
3843                 } else {
3844                     code = CM_ERROR_LOCK_CONFLICT;
3845                     break;
3846                 }
3847             } else if (IS_LOCK_LOST(fileLock)) {
3848                 code = CM_ERROR_BADFD;
3849                 break;
3850             }
3851         }
3852     }
3853
3854     lock_ReleaseRead(&cm_scacheLock);
3855
3856     osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3857               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3858
3859     return code;
3860
3861 #else
3862
3863     return 0;
3864
3865 #endif
3866 }
3867
3868 /* Forward dcl. */
3869 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3870
3871 /* Called with cm_scacheLock write locked */
3872 static cm_file_lock_t * cm_GetFileLock(void) {
3873     cm_file_lock_t * l;
3874
3875     l = (cm_file_lock_t *) cm_freeFileLocks;
3876     if (l) {
3877         osi_QRemove(&cm_freeFileLocks, &l->q);
3878     } else {
3879         l = malloc(sizeof(cm_file_lock_t));
3880         osi_assert(l);
3881     }
3882
3883     memset(l, 0, sizeof(cm_file_lock_t));
3884
3885     return l;
3886 }
3887
3888 /* Called with cm_scacheLock write locked */
3889 static void cm_PutFileLock(cm_file_lock_t *l) {
3890     osi_QAdd(&cm_freeFileLocks, &l->q);
3891 }
3892
3893 /* called with scp->mx held.  May release it during processing, but
3894    leaves it held on exit. */
3895 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
3896                    cm_req_t * reqp) {
3897     long code = 0;
3898     AFSFid tfid;
3899     cm_fid_t cfid;
3900     cm_conn_t * connp;
3901     struct rx_connection * callp;
3902     AFSVolSync volSync;
3903
3904     tfid.Volume = scp->fid.volume;
3905     tfid.Vnode = scp->fid.vnode;
3906     tfid.Unique = scp->fid.unique;
3907     cfid = scp->fid;
3908
3909     osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
3910
3911     lock_ReleaseMutex(&scp->mx);
3912
3913     do {
3914         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
3915         if (code) 
3916             break;
3917
3918         callp = cm_GetRxConn(connp);
3919         code = RXAFS_SetLock(callp, &tfid, lockType,
3920                              &volSync);
3921         rx_PutConnection(callp);
3922
3923     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3924                         NULL, NULL, code));
3925
3926     code = cm_MapRPCError(code, reqp);
3927     if (code) {
3928         osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3929     } else {
3930         osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3931     }
3932
3933     lock_ObtainMutex(&scp->mx);
3934
3935     return code;
3936 }
3937
3938 /* called with scp->mx held.  Releases it during processing */
3939 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
3940                        cm_req_t * reqp) {
3941     long code = 0;
3942     AFSFid tfid;
3943     cm_fid_t cfid;
3944     cm_conn_t * connp;
3945     struct rx_connection * callp;
3946     AFSVolSync volSync;
3947
3948     tfid.Volume = scp->fid.volume;
3949     tfid.Vnode = scp->fid.vnode;
3950     tfid.Unique = scp->fid.unique;
3951     cfid = scp->fid;
3952
3953     lock_ReleaseMutex(&scp->mx);
3954
3955     osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3956
3957     do {
3958         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
3959         if (code) 
3960             break;
3961
3962         callp = cm_GetRxConn(connp);
3963         code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3964         rx_PutConnection(callp);
3965
3966     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3967                         NULL, NULL, code));
3968     code = cm_MapRPCError(code, reqp);
3969     if (code)
3970         osi_Log1(afsd_logp,
3971                  "CALL ReleaseLock FAILURE, code 0x%x", code);
3972     else
3973         osi_Log0(afsd_logp,
3974                  "CALL ReleaseLock SUCCESS");
3975         
3976     lock_ObtainMutex(&scp->mx);
3977
3978     return code;
3979 }
3980
3981 /* called with scp->mx held.  May release it during processing, but
3982    will exit with lock held.
3983
3984    This will return:
3985
3986    - 0 if the user has permission to get the specified lock for the scp
3987
3988    - CM_ERROR_NOACCESS if not
3989
3990    Any other error from cm_SyncOp will be sent down untranslated.
3991
3992    If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
3993    phas_insert (if non-NULL) will receive a boolean value indicating
3994    whether the user has INSERT permission or not.
3995 */
3996 long cm_LockCheckPerms(cm_scache_t * scp,
3997                        int lock_type,
3998                        cm_user_t * userp,
3999                        cm_req_t * reqp,
4000                        int * phas_insert)
4001 {
4002     long rights = 0;
4003     long code = 0, code2 = 0;
4004
4005     /* lock permissions are slightly tricky because of the 'i' bit.
4006        If the user has PRSFS_LOCK, she can read-lock the file.  If the
4007        user has PRSFS_WRITE, she can write-lock the file.  However, if
4008        the user has PRSFS_INSERT, then she can write-lock new files,
4009        but not old ones.  Since we don't have information about
4010        whether a file is new or not, we assume that if the user owns
4011        the scp, then she has the permissions that are granted by
4012        PRSFS_INSERT. */
4013
4014     osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4015              scp, lock_type, userp);
4016
4017     if (lock_type == LockRead)
4018         rights |= PRSFS_LOCK;
4019     else if (lock_type == LockWrite)
4020         rights |= PRSFS_WRITE | PRSFS_LOCK;
4021     else {
4022         /* hmmkay */
4023         osi_assert(FALSE);
4024         return 0;
4025     }
4026
4027     if (phas_insert)
4028         *phas_insert = FALSE;
4029
4030     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4031                      CM_SCACHESYNC_GETSTATUS |
4032                      CM_SCACHESYNC_NEEDCALLBACK);
4033
4034     if (phas_insert && scp->creator == userp) {
4035
4036         /* If this file was created by the user, then we check for
4037            PRSFS_INSERT.  If the file server is recent enough, then
4038            this should be sufficient for her to get a write-lock (but
4039            not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4040            indicates whether a file server supports getting write
4041            locks when the user only has PRSFS_INSERT. 
4042            
4043            If the file was not created by the user we skip the check
4044            because the INSERT bit will not apply to this user even
4045            if it is set.
4046          */
4047
4048         code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4049                          CM_SCACHESYNC_GETSTATUS |
4050                          CM_SCACHESYNC_NEEDCALLBACK);
4051
4052         if (code2 == CM_ERROR_NOACCESS) {
4053             osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4054         } else {
4055             *phas_insert = TRUE;
4056             osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4057         }
4058     }
4059
4060     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4061
4062     osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4063
4064     return code;
4065 }
4066
4067 /* called with scp->mx held */
4068 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4069              LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4070              cm_key_t key,
4071              int allowWait, cm_user_t *userp, cm_req_t *reqp,
4072              cm_file_lock_t **lockpp)
4073 {
4074     long code = 0;
4075     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4076     cm_file_lock_t *fileLock;
4077     osi_queue_t *q;
4078     cm_range_t range;
4079     int wait_unlock = FALSE;
4080     int force_client_lock = FALSE;
4081
4082     osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4083              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4084     osi_Log3(afsd_logp, "... allowWait %d key 0x%x:%x", allowWait, 
4085              (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
4086
4087     /*
4088    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4089
4090    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4091       true:
4092
4093        3.1 If L->LockType is exclusive then there does NOT exist a
4094          ACCEPTED lock M in S->fileLocks such that _a_ in
4095          (M->LOffset,+M->LLength).
4096
4097        3.2 If L->LockType is shared then for each ACCEPTED lock M in
4098          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4099          M->LockType is shared.
4100
4101    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4102
4103        4.1 M->key != Key(C)
4104
4105        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4106          and (M->LOffset,+M->LLength) do not intersect.
4107     */
4108
4109     range.offset = LOffset.QuadPart;
4110     range.length = LLength.QuadPart;
4111
4112     lock_ObtainRead(&cm_scacheLock);
4113
4114     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4115         fileLock =
4116             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4117
4118         if (IS_LOCK_LOST(fileLock)) {
4119             if (fileLock->key == key) {
4120                 code = CM_ERROR_BADFD;
4121                 break;
4122             } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4123                 code = CM_ERROR_WOULDBLOCK;
4124                 wait_unlock = TRUE;
4125                 break;
4126             }
4127         }
4128
4129         /* we don't need to check for deleted locks here since deleted
4130            locks are dequeued from scp->fileLocks */
4131         if (IS_LOCK_ACCEPTED(fileLock) &&
4132            INTERSECT_RANGE(range, fileLock->range)) {
4133
4134             if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4135                 fileLock->lockType != LockRead) {
4136                 wait_unlock = TRUE;
4137                 code = CM_ERROR_WOULDBLOCK;
4138                 break;
4139             }
4140         }
4141     }
4142
4143     lock_ReleaseRead(&cm_scacheLock);
4144
4145     if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4146         if (Which == scp->serverLock ||
4147            (Which == LockRead && scp->serverLock == LockWrite)) {
4148
4149             int has_insert = 0;
4150
4151             /* we already have the lock we need */
4152             osi_Log3(afsd_logp, "   we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]", 
4153                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4154
4155             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4156
4157             /* special case: if we don't have permission to read-lock
4158                the file, then we force a clientside lock.  This is to
4159                compensate for applications that obtain a read-lock for
4160                reading files off of directories that don't grant
4161                read-locks to the user. */
4162             if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4163
4164                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4165                     osi_Log0(afsd_logp, "   User has no read-lock perms, but has INSERT perms.");
4166                     code = 0;
4167                 } else {
4168                     osi_Log0(afsd_logp, "   User has no read-lock perms. Forcing client-side lock");
4169                     force_client_lock = TRUE;
4170                 }
4171             }
4172
4173         } else if ((scp->exclusiveLocks > 0) ||
4174                    (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4175             int has_insert = 0;
4176
4177             /* We are already waiting for some other lock.  We should
4178                wait for the daemon to catch up instead of generating a
4179                flood of SetLock calls. */
4180             osi_Log3(afsd_logp, "   already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4181                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4182
4183             /* see if we have permission to create the lock in the
4184                first place. */
4185             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4186             if (code == 0)
4187                 code = CM_ERROR_WOULDBLOCK;
4188             else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4189
4190                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4191                     osi_Log0(afsd_logp,
4192                              "   User has no read-lock perms, but has INSERT perms.");
4193                     code = CM_ERROR_WOULDBLOCK;
4194                 } else {
4195                     osi_Log0(afsd_logp,
4196                              "   User has no read-lock perms. Forcing client-side lock");
4197                     force_client_lock = TRUE;
4198                 }
4199             }
4200
4201             /* leave any other codes as-is */
4202
4203         } else {
4204             int newLock;
4205             int check_data_version = FALSE;
4206             int has_insert = 0;
4207
4208             /* first check if we have permission to elevate or obtain
4209                the lock. */
4210             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4211             if (code) {
4212                 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4213                     (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4214                     osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
4215                     force_client_lock = TRUE;
4216                 }
4217                 goto check_code;
4218             }
4219
4220             /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4221
4222             if (scp->serverLock == LockRead && Which == LockWrite) {
4223
4224                 /* We want to escalate the lock to a LockWrite.
4225                  * Unfortunately that's not really possible without
4226                  * letting go of the current lock.  But for now we do
4227                  * it anyway. */
4228
4229                 osi_Log0(afsd_logp,
4230                          "   attempting to UPGRADE from LockRead to LockWrite.");
4231                 osi_Log1(afsd_logp,
4232                          "   dataVersion on scp: %d", scp->dataVersion);
4233
4234                 /* we assume at this point (because scp->serverLock
4235                    was valid) that we had a valid server lock. */
4236                 scp->lockDataVersion = scp->dataVersion;
4237                 check_data_version = TRUE;
4238         
4239                 code = cm_IntReleaseLock(scp, userp, reqp);
4240
4241                 if (code) {
4242                     /* We couldn't release the lock */
4243                     goto check_code;
4244                 } else {
4245                     scp->serverLock = -1;
4246                 }
4247             }
4248
4249             /* We need to obtain a server lock of type Which in order
4250              * to assert this file lock */
4251 #ifndef AGGRESSIVE_LOCKS
4252             newLock = Which;
4253 #else
4254             newLock = LockWrite;
4255 #endif
4256
4257             code = cm_IntSetLock(scp, userp, newLock, reqp);
4258
4259 #ifdef AGGRESSIVE_LOCKS
4260             if ((code == CM_ERROR_WOULDBLOCK ||
4261                  code == CM_ERROR_NOACCESS) && newLock != Which) {
4262                 /* we wanted LockRead.  We tried LockWrite. Now try
4263                  * LockRead again */
4264                 newLock = Which;
4265
4266                 /* am I sane? */
4267                 osi_assert(newLock == LockRead);
4268
4269                 code = cm_IntSetLock(scp, userp, newLock, reqp);
4270             }
4271 #endif
4272
4273             if (code == CM_ERROR_NOACCESS) {
4274                 if (Which == LockRead) {
4275                     if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4276                         long tcode;
4277                         /* We requested a read-lock, but we have permission to
4278                          * get a write-lock. Try that */
4279
4280                         tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4281
4282                         if (tcode == 0) {
4283                             newLock = LockWrite;
4284
4285                             osi_Log0(afsd_logp, "   User has 'i' perms and the request was for a LockRead.  Trying to get a LockWrite instead");
4286
4287                             code = cm_IntSetLock(scp, userp, newLock, reqp);
4288                         }
4289                     } else {
4290                         osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
4291                         force_client_lock = TRUE;
4292                     }
4293                 } else if (Which == LockWrite &&
4294                            scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4295                     long tcode;
4296
4297                     /* Special case: if the lock request was for a
4298                      * LockWrite and the user owns the file and we weren't
4299                      * allowed to obtain the serverlock, we either lost a
4300                      * race (the permissions changed from under us), or we
4301                      * have 'i' bits, but we aren't allowed to lock the
4302                      * file. */
4303
4304                     /* check if we lost a race... */
4305                     tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4306
4307                     if (tcode == 0) {
4308                         osi_Log0(afsd_logp, "   User has 'i' perms but can't obtain write locks. Using client-side locks.");
4309                         force_client_lock = TRUE;
4310                     }
4311                 }
4312             }
4313
4314             if (code == 0 && check_data_version &&
4315                scp->dataVersion != scp->lockDataVersion) {
4316                 /* We lost a race.  Although we successfully obtained
4317                  * a lock, someone modified the file in between.  The
4318                  * locks have all been technically lost. */
4319
4320                 osi_Log0(afsd_logp,
4321                          "  Data version mismatch while upgrading lock.");
4322                 osi_Log2(afsd_logp,
4323                          "  Data versions before=%d, after=%d",
4324                          scp->lockDataVersion,
4325                          scp->dataVersion);
4326                 osi_Log1(afsd_logp,
4327                          "  Releasing stale lock for scp 0x%x", scp);
4328
4329                 code = cm_IntReleaseLock(scp, userp, reqp);
4330
4331                 scp->serverLock = -1;
4332
4333                 code = CM_ERROR_INVAL;
4334             } else if (code == 0) {
4335                 scp->serverLock = newLock;
4336                 scp->lockDataVersion = scp->dataVersion;
4337             }
4338
4339             if (code != 0 &&
4340                 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4341                 scp->serverLock == -1) {
4342                 /* Oops. We lost the lock. */
4343                 cm_LockMarkSCacheLost(scp);
4344             }
4345         }
4346     } else if (code == 0) {     /* server locks not enabled */
4347         osi_Log0(afsd_logp,
4348                  "  Skipping server lock for scp");
4349     }
4350
4351  check_code:
4352
4353     if (code != 0 && !force_client_lock) {
4354         /* Special case error translations
4355
4356            Applications don't expect certain errors from a
4357            LockFile/UnlockFile call.  We need to translate some error
4358            code to codes that apps expect and handle. */
4359
4360         /* We shouldn't actually need to handle this case since we
4361            simulate locks for RO scps anyway. */
4362         if (code == CM_ERROR_READONLY) {
4363             osi_Log0(afsd_logp, "   Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4364             code = CM_ERROR_NOACCESS;
4365         }
4366     }
4367
4368     if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4369         force_client_lock) {
4370
4371         /* clear the error if we are forcing a client lock, so we
4372            don't get confused later. */
4373         if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4374             code = 0;
4375
4376         lock_ObtainWrite(&cm_scacheLock);
4377         fileLock = cm_GetFileLock();
4378         lock_ReleaseWrite(&cm_scacheLock);
4379 #ifdef DEBUG
4380         fileLock->fid = scp->fid;
4381 #endif
4382         fileLock->key = key;
4383         fileLock->lockType = Which;
4384         cm_HoldUser(userp);
4385         fileLock->userp = userp;
4386         fileLock->range = range;
4387         fileLock->flags = (code == 0 ? 0 : 
4388                            ((wait_unlock)?
4389                             CM_FILELOCK_FLAG_WAITUNLOCK :
4390                             CM_FILELOCK_FLAG_WAITLOCK));
4391
4392         if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4393             fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4394
4395         fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4396
4397         lock_ObtainWrite(&cm_scacheLock);
4398         osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4399         cm_HoldSCacheNoLock(scp);
4400         fileLock->scp = scp;
4401         osi_QAdd(&cm_allFileLocks, &fileLock->q);
4402         lock_ReleaseWrite(&cm_scacheLock);
4403
4404         if (code != 0) {
4405             *lockpp = fileLock;
4406         }
4407
4408         if (IS_LOCK_CLIENTONLY(fileLock)) {
4409             scp->clientLocks++;
4410         } else if (IS_LOCK_ACCEPTED(fileLock)) {
4411             if (Which == LockRead)
4412                 scp->sharedLocks++;
4413             else
4414                 scp->exclusiveLocks++;
4415         }
4416
4417         osi_Log3(afsd_logp,
4418                  "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4419                  fileLock, fileLock->flags, scp);
4420         osi_Log4(afsd_logp,
4421                  "   exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4422                  scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4423                  (int)(signed char) scp->serverLock);
4424     } else {
4425         osi_Log1(afsd_logp,
4426                  "cm_Lock Rejecting lock (code = 0x%x)", code);
4427     }
4428
4429     return code;
4430 }
4431
4432 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
4433
4434 /* Called with scp->mx held */
4435 long cm_UnlockByKey(cm_scache_t * scp,
4436                     cm_key_t key,
4437                     int flags,
4438                     cm_user_t * userp,
4439                      cm_req_t * reqp)
4440 {
4441     long code = 0;
4442     cm_file_lock_t *fileLock;
4443     osi_queue_t *q, *qn;
4444     int n_unlocks = 0;
4445
4446     osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key 0x%x:%x flags=0x%x",
4447              scp,
4448              (unsigned long)(key >> 32),
4449              (unsigned long)(key & 0xffffffff),
4450              flags);
4451
4452     lock_ObtainWrite(&cm_scacheLock);
4453
4454     for (q = scp->fileLocksH; q; q = qn) {
4455         qn = osi_QNext(q);
4456
4457         fileLock = (cm_file_lock_t *)
4458             ((char *) q - offsetof(cm_file_lock_t, fileq));
4459
4460 #ifdef DEBUG
4461         osi_Log4(afsd_logp, "   Checking lock[0x%x] range[%d,+%d] type[%d]",
4462                  fileLock,
4463                  (unsigned long) fileLock->range.offset,
4464                  (unsigned long) fileLock->range.length,
4465                 fileLock->lockType);
4466         osi_Log3(afsd_logp, "     key[0x%x:%x] flags[0x%x]",
4467                  (unsigned long)(fileLock->key >> 32),
4468                  (unsigned long)(fileLock->key & 0xffffffff),
4469                  fileLock->flags);
4470
4471         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4472             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4473             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4474                      fileLock->fid.cell,
4475                      fileLock->fid.volume,
4476                      fileLock->fid.vnode,
4477                      fileLock->fid.unique);
4478             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4479                      fileLock->scp->fid.cell,
4480                      fileLock->scp->fid.volume,
4481                      fileLock->scp->fid.vnode,
4482                      fileLock->scp->fid.unique);
4483             osi_assert(FALSE);
4484         }
4485 #endif
4486
4487         if (!IS_LOCK_DELETED(fileLock) &&
4488             cm_KeyEquals(fileLock->key, key, flags)) {
4489             osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4490                     fileLock->range.offset,
4491                     fileLock->range.length,
4492                     fileLock->lockType);
4493
4494             if (scp->fileLocksT == q)
4495                 scp->fileLocksT = osi_QPrev(q);
4496             osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4497
4498             if (IS_LOCK_CLIENTONLY(fileLock)) {
4499                 scp->clientLocks--;
4500             } else if (IS_LOCK_ACCEPTED(fileLock)) {
4501                 if (fileLock->lockType == LockRead)
4502                     scp->sharedLocks--;
4503                 else
4504                     scp->exclusiveLocks--;
4505             }
4506
4507             fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4508
4509             cm_ReleaseUser(fileLock->userp);
4510             cm_ReleaseSCacheNoLock(scp);
4511
4512             fileLock->userp = NULL;
4513             fileLock->scp = NULL;
4514
4515             n_unlocks++;
4516         }
4517     }
4518
4519     lock_ReleaseWrite(&cm_scacheLock);
4520
4521     if (n_unlocks == 0) {
4522         osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4523         osi_Log3(afsd_logp, "   Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4524                  scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4525         
4526         return 0;
4527     }
4528
4529     osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4530
4531     osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4532     osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4533     osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4534
4535     if (!SERVERLOCKS_ENABLED(scp)) {
4536         osi_Log0(afsd_logp, "  Skipping server lock for scp");
4537         goto done;
4538     }
4539
4540     /* Ideally we would go through the rest of the locks to determine
4541      * if one or more locks that were formerly in WAITUNLOCK can now
4542      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4543      * scp->sharedLocks accordingly.  However, the retrying of locks
4544      * in that manner is done cm_RetryLock() manually.
4545      */
4546
4547     if (scp->serverLock == LockWrite &&
4548         scp->exclusiveLocks == 0 &&
4549         scp->sharedLocks > 0) {
4550
4551         /* The serverLock should be downgraded to LockRead */
4552         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
4553
4554         /* since scp->serverLock looked sane, we are going to assume
4555            that we have a valid server lock. */
4556         scp->lockDataVersion = scp->dataVersion;
4557         osi_Log1(afsd_logp, "  dataVersion on scp = %d", scp->dataVersion);
4558
4559         code = cm_IntReleaseLock(scp, userp, reqp);
4560
4561         if (code) {
4562             /* so we couldn't release it.  Just let the lock be for now */
4563             code = 0;
4564             goto done;
4565         } else {
4566             scp->serverLock = -1;
4567         }
4568
4569         code = cm_IntSetLock(scp, userp, LockRead, reqp);
4570
4571         if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4572             scp->serverLock = LockRead;
4573         } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4574             /* We lost a race condition.  Although we have a valid
4575                lock on the file, the data has changed and essentially
4576                we have lost the lock we had during the transition. */
4577
4578             osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4579             osi_Log2(afsd_logp, "  Data versions before=%d, after=%d",
4580                      scp->lockDataVersion,
4581                      scp->dataVersion);
4582             
4583             code = cm_IntReleaseLock(scp, userp, reqp);
4584
4585             code = CM_ERROR_INVAL;
4586             scp->serverLock = -1;
4587         }
4588
4589         if (code != 0 &&
4590             (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4591                 (scp->serverLock == -1)) {
4592                 /* Oopsie */
4593                 cm_LockMarkSCacheLost(scp);
4594             }
4595
4596         /* failure here has no bearing on the return value of
4597            cm_Unlock() */
4598         code = 0;
4599
4600     } else if (scp->serverLock != (-1) &&
4601               scp->exclusiveLocks == 0 &&
4602               scp->sharedLocks == 0) {
4603         /* The serverLock should be released entirely */
4604
4605         code = cm_IntReleaseLock(scp, userp, reqp);
4606
4607         if (code == 0)
4608             scp->serverLock = (-1);
4609     }
4610
4611  done:
4612
4613     osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4614     osi_Log4(afsd_logp, "   Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4615              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4616              (int)(signed char) scp->serverLock);
4617
4618     return code;
4619 }
4620
4621 long cm_Unlock(cm_scache_t *scp, 
4622                unsigned char sLockType,
4623                LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4624                cm_key_t key, 
4625                cm_user_t *userp, 
4626                cm_req_t *reqp)
4627 {
4628     long code = 0;
4629     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4630     cm_file_lock_t *fileLock;
4631     osi_queue_t *q;
4632     int release_userp = FALSE;
4633
4634     osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4635              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4636     osi_Log2(afsd_logp, "... key 0x%x:%x",
4637              (unsigned long) (key >> 32), (unsigned long) (key & 0xffffffff));
4638
4639     lock_ObtainRead(&cm_scacheLock);
4640
4641     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4642         fileLock = (cm_file_lock_t *)
4643             ((char *) q - offsetof(cm_file_lock_t, fileq));
4644
4645 #ifdef DEBUG
4646         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4647             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4648             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4649                      fileLock->fid.cell,
4650                      fileLock->fid.volume,
4651                      fileLock->fid.vnode,
4652                      fileLock->fid.unique);
4653             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4654                      fileLock->scp->fid.cell,
4655                      fileLock->scp->fid.volume,
4656                      fileLock->scp->fid.vnode,
4657                      fileLock->scp->fid.unique);
4658             osi_assert(FALSE);
4659         }
4660 #endif
4661         if (!IS_LOCK_DELETED(fileLock) &&
4662             fileLock->key == key &&
4663             fileLock->range.offset == LOffset.QuadPart &&
4664             fileLock->range.length == LLength.QuadPart) {
4665             break;
4666         }
4667     }
4668
4669     if (!q) {
4670         osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
4671         
4672         lock_ReleaseRead(&cm_scacheLock);
4673
4674         /* The lock didn't exist anyway. *shrug* */
4675         return 0;
4676     }
4677
4678     lock_ReleaseRead(&cm_scacheLock);
4679
4680     /* discard lock record */
4681     lock_ObtainWrite(&cm_scacheLock);
4682     if (scp->fileLocksT == q)
4683         scp->fileLocksT = osi_QPrev(q);
4684     osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4685
4686     /*
4687      * Don't delete it here; let the daemon delete it, to simplify
4688      * the daemon's traversal of the list.
4689      */
4690
4691     if (IS_LOCK_CLIENTONLY(fileLock)) {
4692         scp->clientLocks--;
4693     } else if (IS_LOCK_ACCEPTED(fileLock)) {
4694         if (fileLock->lockType == LockRead)
4695             scp->sharedLocks--;
4696         else
4697             scp->exclusiveLocks--;
4698     }
4699
4700     fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4701     if (userp != NULL) {
4702         cm_ReleaseUser(fileLock->userp);
4703     } else {
4704         userp = fileLock->userp;
4705         release_userp = TRUE;
4706     }
4707     fileLock->userp = NULL;
4708     cm_ReleaseSCacheNoLock(scp);
4709     fileLock->scp = NULL;
4710     lock_ReleaseWrite(&cm_scacheLock);
4711
4712     if (!SERVERLOCKS_ENABLED(scp)) {
4713         osi_Log0(afsd_logp, "   Skipping server locks for scp");
4714         goto done;
4715     }
4716
4717     /* Ideally we would go through the rest of the locks to determine
4718      * if one or more locks that were formerly in WAITUNLOCK can now
4719      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4720      * scp->sharedLocks accordingly.  However, the retrying of locks
4721      * in that manner is done cm_RetryLock() manually.
4722      */
4723
4724     if (scp->serverLock == LockWrite &&
4725         scp->exclusiveLocks == 0 &&
4726         scp->sharedLocks > 0) {
4727
4728         /* The serverLock should be downgraded to LockRead */
4729         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
4730
4731         /* Since we already had a lock, we assume that there is a
4732            valid server lock. */
4733         scp->lockDataVersion = scp->dataVersion;
4734         osi_Log1(afsd_logp, "   dataVersion on scp is %d", scp->dataVersion);
4735
4736         /* before we downgrade, make sure that we have enough
4737            permissions to get the read lock. */
4738         code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
4739         if (code != 0) {
4740
4741             osi_Log0(afsd_logp, "  SKIPPING downgrade because user doesn't have perms to get downgraded lock");
4742
4743             code = 0;
4744             goto done;
4745         }
4746
4747         code = cm_IntReleaseLock(scp, userp, reqp);
4748
4749         if (code) {
4750             /* so we couldn't release it.  Just let the lock be for now */
4751             code = 0;
4752             goto done;
4753         } else {
4754             scp->serverLock = -1;
4755         }
4756
4757         code = cm_IntSetLock(scp, userp, LockRead, reqp);
4758
4759         if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4760             scp->serverLock = LockRead;
4761         } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4762             /* Lost a race.  We obtained a new lock, but that is
4763                meaningless since someone modified the file
4764                inbetween. */
4765
4766             osi_Log0(afsd_logp,
4767                      "Data version mismatch while downgrading lock");
4768             osi_Log2(afsd_logp,
4769                      "  Data versions before=%d, after=%d",
4770                      scp->lockDataVersion,
4771                      scp->dataVersion);
4772             
4773             code = cm_IntReleaseLock(scp, userp, reqp);
4774
4775             scp->serverLock = -1;
4776             code = CM_ERROR_INVAL;
4777         }
4778
4779         if (code != 0 &&
4780             (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4781                 (scp->serverLock == -1)) {
4782                 /* Oopsie */
4783                 cm_LockMarkSCacheLost(scp);
4784             }
4785
4786         /* failure here has no bearing on the return value of
4787            cm_Unlock() */
4788         code = 0;
4789
4790     } else if (scp->serverLock != (-1) &&
4791               scp->exclusiveLocks == 0 &&
4792               scp->sharedLocks == 0) {
4793         /* The serverLock should be released entirely */
4794
4795         code = cm_IntReleaseLock(scp, userp, reqp);
4796
4797         if (code == 0) {
4798             scp->serverLock = (-1);
4799         }
4800     }
4801
4802     if (release_userp)
4803         cm_ReleaseUser(userp);
4804
4805  done:
4806
4807     osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
4808     osi_Log4(afsd_logp, "  leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4809              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4810              (int)(signed char) scp->serverLock);
4811
4812     return code;
4813 }
4814
4815 /* called with scp->mx held */
4816 static void cm_LockMarkSCacheLost(cm_scache_t * scp)
4817 {
4818     cm_file_lock_t *fileLock;
4819     osi_queue_t *q;
4820
4821     osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
4822
4823 #ifdef DEBUG
4824     /* With the current code, we can't lose a lock on a RO scp */
4825     osi_assert(!(scp->flags & CM_SCACHEFLAG_RO));
4826 #endif
4827
4828     /* cm_scacheLock needed because we are modifying fileLock->flags */
4829     lock_ObtainWrite(&cm_scacheLock);
4830
4831     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4832         fileLock = 
4833             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4834
4835         if (IS_LOCK_ACTIVE(fileLock) &&
4836             !IS_LOCK_CLIENTONLY(fileLock)) {
4837             if (fileLock->lockType == LockRead)
4838                 scp->sharedLocks--;
4839             else
4840                 scp->exclusiveLocks--;
4841
4842             fileLock->flags |= CM_FILELOCK_FLAG_LOST;
4843         }
4844     }
4845
4846     scp->serverLock = -1;
4847     scp->lockDataVersion = -1;
4848     lock_ReleaseWrite(&cm_scacheLock);
4849 }
4850
4851 /* Called with no relevant locks held */
4852 void cm_CheckLocks()
4853 {
4854     osi_queue_t *q, *nq;
4855     cm_file_lock_t *fileLock;
4856     cm_req_t req;
4857     AFSFid tfid;
4858     AFSVolSync volSync;
4859     cm_conn_t *connp;
4860     long code;
4861     struct rx_connection * callp;
4862     cm_scache_t * scp;
4863
4864     cm_InitReq(&req);
4865
4866     lock_ObtainWrite(&cm_scacheLock);
4867
4868     cm_lockRefreshCycle++;
4869
4870     osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
4871
4872     for (q = cm_allFileLocks; q; q = nq) {
4873         fileLock = (cm_file_lock_t *) q;
4874         nq = osi_QNext(q);
4875         code = -1;
4876
4877         if (IS_LOCK_DELETED(fileLock)) {
4878
4879             osi_QRemove(&cm_allFileLocks, q);
4880             cm_PutFileLock(fileLock);
4881
4882         } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
4883
4884             /* Server locks must have been enabled for us to have
4885                received an active non-client-only lock. */
4886             osi_assert(cm_enableServerLocks);
4887
4888             scp = fileLock->scp;
4889             osi_assert(scp != NULL);
4890
4891             cm_HoldSCacheNoLock(scp);
4892
4893 #ifdef DEBUG
4894             if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4895                 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4896                 osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4897                          fileLock->fid.cell,
4898                          fileLock->fid.volume,
4899                          fileLock->fid.vnode,
4900                          fileLock->fid.unique);
4901                 osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4902                          fileLock->scp->fid.cell,
4903                          fileLock->scp->fid.volume,
4904                          fileLock->scp->fid.vnode,
4905                          fileLock->scp->fid.unique);
4906                 osi_assert(FALSE);
4907             }
4908 #endif
4909             /* Server locks are extended once per scp per refresh
4910                cycle. */
4911             if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
4912
4913                 int scp_done = FALSE;
4914
4915                 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
4916
4917                 lock_ReleaseWrite(&cm_scacheLock);
4918                 lock_ObtainMutex(&scp->mx);
4919
4920                 /* did the lock change while we weren't holding the lock? */
4921                 if (!IS_LOCK_ACTIVE(fileLock))
4922                     goto post_syncopdone;
4923
4924                 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
4925                                  CM_SCACHESYNC_NEEDCALLBACK
4926                                  | CM_SCACHESYNC_GETSTATUS
4927                                  | CM_SCACHESYNC_LOCK);
4928
4929                 if (code) {
4930                     osi_Log1(smb_logp,
4931                              "cm_CheckLocks SyncOp failure code 0x%x", code);
4932                     goto post_syncopdone;
4933                 }
4934
4935                 /* cm_SyncOp releases scp->mx during which the lock
4936                    may get released. */
4937                 if (!IS_LOCK_ACTIVE(fileLock))
4938                     goto pre_syncopdone;
4939
4940                 if (scp->serverLock != -1) {
4941                     cm_fid_t cfid;
4942                     cm_user_t * userp;
4943
4944                     tfid.Volume = scp->fid.volume;
4945                     tfid.Vnode = scp->fid.vnode;
4946                     tfid.Unique = scp->fid.unique;
4947                     cfid = scp->fid;
4948                     userp = fileLock->userp;
4949                     
4950                     osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d", 
4951                              fileLock,
4952                              scp,
4953                              (int) scp->serverLock);
4954
4955                     lock_ReleaseMutex(&scp->mx);
4956
4957                     do {
4958                         code = cm_ConnFromFID(&cfid, userp,
4959                                        &req, &connp);
4960                         if (code) 
4961                             break;
4962
4963                         callp = cm_GetRxConn(connp);
4964                         code = RXAFS_ExtendLock(callp, &tfid,
4965                                                 &volSync);
4966                         rx_PutConnection(callp);
4967
4968                         osi_Log1(afsd_logp, "   ExtendLock returns %d", code);
4969
4970                     } while (cm_Analyze(connp, userp, &req,
4971                                         &cfid, &volSync, NULL, NULL,
4972                                         code));
4973
4974                     code = cm_MapRPCError(code, &req);
4975
4976                     lock_ObtainMutex(&scp->mx);
4977
4978                     if (code) {
4979                         osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
4980                     } else {
4981                         osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
4982                         scp->lockDataVersion = scp->dataVersion;
4983                     }
4984
4985                     if ((code == EINVAL || code == CM_ERROR_INVAL) &&
4986                         scp->lockDataVersion == scp->dataVersion) {
4987                         int lockType;
4988
4989                         lockType =
4990                             (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
4991
4992                         /* we might still have a chance to obtain a
4993                            new lock */
4994
4995                         code = cm_IntSetLock(scp, userp, lockType, &req);
4996
4997                         if (code) {
4998                             code = CM_ERROR_INVAL;
4999                         } else if (scp->lockDataVersion != scp->dataVersion) {
5000
5001                             /* now check if we still have the file at
5002                                the right data version. */
5003                             osi_Log1(afsd_logp,
5004                                      "Data version mismatch on scp 0x%p",
5005                                      scp);
5006                             osi_Log2(afsd_logp,
5007                                      "   Data versions: before=%d, after=%d",
5008                                      scp->lockDataVersion,
5009                                      scp->dataVersion);
5010
5011                             code = cm_IntReleaseLock(scp, userp, &req);
5012
5013                             code = CM_ERROR_INVAL;
5014                         }
5015                     }
5016
5017                     if (code == EINVAL || code == CM_ERROR_INVAL) {
5018                         cm_LockMarkSCacheLost(scp);
5019                     }
5020
5021                 } else {
5022                     /* interestingly, we have found an active lock
5023                        belonging to an scache that has no
5024                        serverLock */
5025                     cm_LockMarkSCacheLost(scp);
5026                 }
5027
5028                 scp_done = TRUE;
5029
5030             pre_syncopdone:
5031
5032                 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5033
5034             post_syncopdone:
5035                 lock_ReleaseMutex(&scp->mx);
5036
5037                 lock_ObtainWrite(&cm_scacheLock);
5038
5039                 if (code == 0) {
5040                     fileLock->lastUpdate = time(NULL);
5041                 }
5042                 
5043                 if (scp_done)
5044                     scp->lastRefreshCycle = cm_lockRefreshCycle;
5045
5046             } else {
5047                 /* we have already refreshed the locks on this scp */
5048                 fileLock->lastUpdate = time(NULL);
5049             }
5050
5051             cm_ReleaseSCacheNoLock(scp);
5052
5053         } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5054             /* TODO: Check callbacks */
5055         }
5056     }
5057
5058     lock_ReleaseWrite(&cm_scacheLock);
5059     osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5060 }
5061
5062 /* NOT called with scp->mx held. */
5063 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5064 {
5065     long code = 0;
5066     cm_scache_t *scp = NULL;
5067     cm_file_lock_t *fileLock;
5068     osi_queue_t *q;
5069     cm_req_t req;
5070     int newLock = -1;
5071     int force_client_lock = FALSE;
5072     int has_insert = FALSE;
5073     int check_data_version = FALSE;
5074
5075     cm_InitReq(&req);
5076
5077     if (client_is_dead) {
5078         code = CM_ERROR_TIMEDOUT;
5079         goto updateLock;
5080     }
5081
5082     lock_ObtainRead(&cm_scacheLock);
5083
5084     osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5085     osi_Log4(afsd_logp, "    offset(%x:%x) length(%x:%x)",
5086              (unsigned)(oldFileLock->range.offset >> 32),
5087              (unsigned)(oldFileLock->range.offset & 0xffffffff),
5088              (unsigned)(oldFileLock->range.length >> 32),
5089              (unsigned)(oldFileLock->range.length & 0xffffffff));
5090     osi_Log3(afsd_logp, "    key(%x:%x) flags=%x",
5091              (unsigned)(oldFileLock->key >> 32),
5092              (unsigned)(oldFileLock->key & 0xffffffff),
5093              (unsigned)(oldFileLock->flags));
5094
5095     /* if the lock has already been granted, then we have nothing to do */
5096     if (IS_LOCK_ACTIVE(oldFileLock)) {
5097         lock_ReleaseRead(&cm_scacheLock);
5098         osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5099         return 0;
5100     }
5101
5102     /* we can't do anything with lost or deleted locks at the moment. */
5103     if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5104         code = CM_ERROR_BADFD;
5105         osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5106         lock_ReleaseRead(&cm_scacheLock);
5107         goto updateLock;
5108     }
5109
5110     scp = oldFileLock->scp;
5111
5112     osi_assert(scp != NULL);
5113
5114     lock_ReleaseRead(&cm_scacheLock);
5115     lock_ObtainMutex(&scp->mx);
5116
5117     code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5118                              oldFileLock->userp,
5119                              &req, &has_insert);
5120
5121     if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5122         if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5123         force_client_lock = TRUE;
5124         }
5125         code = 0;
5126     } else if (code) {
5127         lock_ReleaseMutex(&scp->mx);
5128         return code;
5129     }
5130
5131     lock_ObtainWrite(&cm_scacheLock);
5132
5133     /* Check if we already have a sufficient server lock to allow this
5134        lock to go through. */
5135     if (IS_LOCK_WAITLOCK(oldFileLock) &&
5136         (!SERVERLOCKS_ENABLED(scp) ||
5137          scp->serverLock == oldFileLock->lockType ||
5138          scp->serverLock == LockWrite)) {
5139
5140         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5141
5142         if (SERVERLOCKS_ENABLED(scp)) {
5143             osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock.  Granting",
5144                      (int) scp->serverLock);
5145         } else {
5146             osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5147         }
5148
5149         lock_ReleaseWrite(&cm_scacheLock);
5150         lock_ReleaseMutex(&scp->mx);
5151
5152         return 0;
5153     }
5154
5155     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5156
5157         /* check if the conflicting locks have dissappeared already */
5158         for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5159
5160             fileLock = (cm_file_lock_t *)
5161                 ((char *) q - offsetof(cm_file_lock_t, fileq));
5162
5163             if (IS_LOCK_LOST(fileLock)) {
5164                 if (fileLock->key == oldFileLock->key) {
5165                     code = CM_ERROR_BADFD;
5166                     oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5167                     osi_Log1(afsd_logp, "    found lost lock %p for same key.  Marking lock as lost",
5168                              fileLock);
5169                     break;
5170                 } else if (fileLock->lockType == LockWrite &&
5171                            INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5172                     osi_Log1(afsd_logp, "    found conflicting LOST lock %p", fileLock);
5173                     code = CM_ERROR_WOULDBLOCK;
5174                     break;
5175                 }
5176             }
5177
5178             if (IS_LOCK_ACCEPTED(fileLock) &&
5179                 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5180
5181                 if (oldFileLock->lockType != LockRead ||
5182                    fileLock->lockType != LockRead) {
5183
5184                     osi_Log1(afsd_logp, "    found conflicting lock %p", fileLock);
5185                     code = CM_ERROR_WOULDBLOCK;
5186                     break;
5187                 }
5188             }
5189         }
5190     }
5191
5192     if (code != 0) {
5193         lock_ReleaseWrite(&cm_scacheLock);
5194         lock_ReleaseMutex(&scp->mx);
5195
5196         goto handleCode;
5197     }
5198
5199     /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5200        If it is WAITUNLOCK, then we didn't find any conflicting lock
5201        but we haven't verfied whether the serverLock is sufficient to
5202        assert it.  If it is WAITLOCK, then the serverLock is
5203        insufficient to assert it. Eitherway, we are ready to accept
5204        the lock as either ACTIVE or WAITLOCK depending on the
5205        serverLock. */
5206
5207     /* First, promote the WAITUNLOCK to a WAITLOCK */
5208     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5209         if (oldFileLock->lockType == LockRead)
5210             scp->sharedLocks++;
5211         else
5212             scp->exclusiveLocks++;
5213
5214         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5215         oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5216     }
5217
5218     osi_assert(IS_LOCK_WAITLOCK(oldFileLock));
5219
5220     if (force_client_lock ||
5221         !SERVERLOCKS_ENABLED(scp) ||
5222         scp->serverLock == oldFileLock->lockType ||
5223         (oldFileLock->lockType == LockRead &&
5224          scp->serverLock == LockWrite)) {
5225
5226         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5227
5228         if ((force_client_lock ||
5229              !SERVERLOCKS_ENABLED(scp)) &&
5230             !IS_LOCK_CLIENTONLY(oldFileLock)) {
5231
5232             oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5233
5234             if (oldFileLock->lockType == LockRead)
5235                 scp->sharedLocks--;
5236             else
5237                 scp->exclusiveLocks--;
5238
5239             scp->clientLocks++;
5240         }
5241
5242         lock_ReleaseWrite(&cm_scacheLock);
5243         lock_ReleaseMutex(&scp->mx);
5244
5245         return 0;
5246
5247     } else {
5248         cm_user_t * userp;
5249
5250         code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5251                          CM_SCACHESYNC_NEEDCALLBACK
5252                          | CM_SCACHESYNC_GETSTATUS
5253                          | CM_SCACHESYNC_LOCK);
5254         if (code) {
5255             osi_Log1(smb_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5256             lock_ReleaseWrite(&cm_scacheLock);
5257             goto post_syncopdone;
5258         }
5259
5260         if (!IS_LOCK_WAITLOCK(oldFileLock))
5261             goto pre_syncopdone;
5262
5263         userp = oldFileLock->userp;
5264
5265 #ifndef AGGRESSIVE_LOCKS
5266         newLock = oldFileLock->lockType;
5267 #else
5268         newLock = LockWrite;
5269 #endif
5270
5271         if (has_insert) {
5272             /* if has_insert is non-zero, then:
5273                - the lock a LockRead
5274                - we don't have permission to get a LockRead
5275                - we do have permission to get a LockWrite
5276                - the server supports VICED_CAPABILITY_WRITELOCKACL
5277             */
5278
5279             newLock = LockWrite;
5280         }
5281
5282         lock_ReleaseWrite(&cm_scacheLock);
5283
5284         /* when we get here, either we have a read-lock and want a
5285            write-lock or we don't have any locks and we want some
5286            lock. */
5287
5288         if (scp->serverLock == LockRead) {
5289
5290             osi_assert(newLock == LockWrite);
5291
5292             osi_Log0(afsd_logp, "  Attempting to UPGRADE from LockRead to LockWrite");
5293
5294             scp->lockDataVersion = scp->dataVersion;
5295             check_data_version = TRUE;
5296
5297             code = cm_IntReleaseLock(scp, userp, &req);
5298
5299             if (code)
5300                 goto pre_syncopdone;
5301             else
5302                 scp->serverLock = -1;
5303         }
5304
5305         code = cm_IntSetLock(scp, userp, newLock, &req);
5306
5307         if (code == 0) {
5308             if (scp->dataVersion != scp->lockDataVersion) {
5309                 /* we lost a race.  too bad */
5310
5311                 osi_Log0(afsd_logp,
5312                          "  Data version mismatch while upgrading lock.");
5313                 osi_Log2(afsd_logp,
5314                          "  Data versions before=%d, after=%d",
5315                          scp->lockDataVersion,
5316                          scp->dataVersion);
5317                 osi_Log1(afsd_logp,
5318                          "  Releasing stale lock for scp 0x%x", scp);
5319
5320                 code = cm_IntReleaseLock(scp, userp, &req);
5321
5322                 scp->serverLock = -1;
5323
5324                 code = CM_ERROR_INVAL;
5325
5326                 cm_LockMarkSCacheLost(scp);
5327             } else {
5328                 scp->serverLock = newLock;
5329             }
5330         }
5331
5332     pre_syncopdone:
5333         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5334     post_syncopdone:
5335         ;
5336     }
5337
5338   handleCode:
5339     if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5340         lock_ObtainWrite(&cm_scacheLock);
5341         if (scp->fileLocksT == &oldFileLock->fileq)
5342             scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5343         osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5344         lock_ReleaseWrite(&cm_scacheLock);
5345     }
5346     lock_ReleaseMutex(&scp->mx);
5347
5348   updateLock:
5349     lock_ObtainWrite(&cm_scacheLock);
5350     if (code == 0) {
5351         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5352     } else if (code != CM_ERROR_WOULDBLOCK) {
5353         oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5354         cm_ReleaseUser(oldFileLock->userp);
5355         oldFileLock->userp = NULL;
5356         if (oldFileLock->scp) {
5357             cm_ReleaseSCacheNoLock(oldFileLock->scp);
5358             oldFileLock->scp = NULL;
5359         }
5360     }
5361     lock_ReleaseWrite(&cm_scacheLock);
5362
5363     return code;
5364 }
5365
5366 cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
5367 {
5368 #ifdef DEBUG
5369     osi_assert((process_id & 0xffffffff) == process_id);
5370     osi_assert((session_id & 0xffff) == session_id);
5371     osi_assert((file_id & 0xffff) == file_id);
5372 #endif
5373
5374     return 
5375         (((cm_key_t) (process_id & 0xffffffff)) << 32) |
5376         (((cm_key_t) (session_id & 0xffff)) << 16) |
5377         (((cm_key_t) (file_id & 0xffff)));
5378 }
5379
5380 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags)
5381 {
5382     if (flags & CM_UNLOCK_BY_FID) {
5383         return ((k1 & 0xffffffff) == (k2 & 0xffffffff));
5384     } else {
5385         return (k1 == k2);
5386     }
5387 }
5388
5389 void cm_ReleaseAllLocks(void)
5390 {
5391     cm_scache_t *scp;
5392     cm_req_t req;
5393     cm_user_t *userp;
5394     cm_key_t   key;
5395     cm_file_lock_t *fileLock;
5396     unsigned int i;
5397
5398     for (i = 0; i < cm_data.scacheHashTableSize; i++)
5399     {
5400         for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5401             while (scp->fileLocksH != NULL) {
5402                 lock_ObtainMutex(&scp->mx);
5403                 lock_ObtainWrite(&cm_scacheLock);
5404                 if (!scp->fileLocksH) {
5405                     lock_ReleaseWrite(&cm_scacheLock);
5406                     lock_ReleaseMutex(&scp->mx);
5407                     break;
5408                 }
5409                 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5410                 userp = fileLock->userp;
5411                 cm_HoldUser(userp);
5412                 key = fileLock->key;
5413                 cm_HoldSCacheNoLock(scp);
5414                 lock_ReleaseWrite(&cm_scacheLock);
5415                 cm_UnlockByKey(scp, key, 0, userp, &req);
5416                 cm_ReleaseSCache(scp);
5417                 cm_ReleaseUser(userp);
5418                 lock_ReleaseMutex(&scp->mx);
5419             }
5420         }
5421     }
5422 }