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