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