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