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