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