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