DEVEL15-windows-multi-fix-20061002
[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
2210     /* make the attr structure */
2211     cm_StatusFromAttr(&afsInStatus, scp, attrp);
2212
2213     tfid.Volume = scp->fid.volume;
2214     tfid.Vnode = scp->fid.vnode;
2215     tfid.Unique = scp->fid.unique;
2216
2217     lock_ReleaseMutex(&scp->mx);
2218     if (code) 
2219         return code;
2220
2221     /* now make the RPC */
2222     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2223     do {
2224         code = cm_Conn(&scp->fid, userp, reqp, &connp);
2225         if (code) 
2226             continue;
2227
2228         callp = cm_GetRxConn(connp);
2229         code = RXAFS_StoreStatus(callp, &tfid,
2230                                   &afsInStatus, &afsOutStatus, &volSync);
2231         rx_PutConnection(callp);
2232
2233     } while (cm_Analyze(connp, userp, reqp,
2234                          &scp->fid, &volSync, NULL, NULL, code));
2235     code = cm_MapRPCError(code, reqp);
2236
2237     if (code)
2238         osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2239     else
2240         osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2241
2242     lock_ObtainMutex(&scp->mx);
2243     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2244     if (code == 0)
2245         cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
2246                         CM_MERGEFLAG_FORCE);
2247         
2248     /* if we're changing the mode bits, discard the ACL cache, 
2249      * since we changed the mode bits.
2250      */
2251     if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
2252     lock_ReleaseMutex(&scp->mx);
2253     return code;
2254 }       
2255
2256 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2257                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2258 {       
2259     cm_conn_t *connp;
2260     long code;
2261     AFSFid dirAFSFid;
2262     cm_callbackRequest_t cbReq;
2263     AFSFid newAFSFid;
2264     cm_fid_t newFid;
2265     cm_scache_t *scp;
2266     int didEnd;
2267     AFSStoreStatus inStatus;
2268     AFSFetchStatus updatedDirStatus;
2269     AFSFetchStatus newFileStatus;
2270     AFSCallBack newFileCallback;
2271     AFSVolSync volSync;
2272     struct rx_connection * callp;
2273
2274     /* can't create names with @sys in them; must expand it manually first.
2275      * return "invalid request" if they try.
2276      */
2277     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2278         return CM_ERROR_ATSYS;
2279     }
2280
2281     /* before starting the RPC, mark that we're changing the file data, so
2282      * that someone who does a chmod will know to wait until our call
2283      * completes.
2284      */
2285     lock_ObtainMutex(&dscp->mx);
2286     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2287     if (code == 0) {
2288         cm_StartCallbackGrantingCall(NULL, &cbReq);
2289     }
2290     lock_ReleaseMutex(&dscp->mx);
2291     if (code) {
2292         return code;
2293     }
2294     didEnd = 0;
2295
2296     cm_StatusFromAttr(&inStatus, NULL, attrp);
2297
2298     /* try the RPC now */
2299     osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2300     do {
2301         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2302         if (code) 
2303             continue;
2304
2305         dirAFSFid.Volume = dscp->fid.volume;
2306         dirAFSFid.Vnode = dscp->fid.vnode;
2307         dirAFSFid.Unique = dscp->fid.unique;
2308
2309         callp = cm_GetRxConn(connp);
2310         code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2311                                  &inStatus, &newAFSFid, &newFileStatus,
2312                                  &updatedDirStatus, &newFileCallback,
2313                                  &volSync);
2314         rx_PutConnection(callp);
2315
2316     } while (cm_Analyze(connp, userp, reqp,
2317                          &dscp->fid, &volSync, NULL, &cbReq, code));
2318     code = cm_MapRPCError(code, reqp);
2319         
2320     if (code)
2321         osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2322     else
2323         osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2324
2325     lock_ObtainMutex(&dscp->mx);
2326     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2327     if (code == 0) {
2328         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2329     }
2330     lock_ReleaseMutex(&dscp->mx);
2331
2332     /* now try to create the file's entry, too, but be careful to 
2333      * make sure that we don't merge in old info.  Since we weren't locking
2334      * out any requests during the file's creation, we may have pretty old
2335      * info.
2336      */
2337     if (code == 0) {
2338         newFid.cell = dscp->fid.cell;
2339         newFid.volume = dscp->fid.volume;
2340         newFid.vnode = newAFSFid.Vnode;
2341         newFid.unique = newAFSFid.Unique;
2342         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2343         if (code == 0) {
2344             lock_ObtainMutex(&scp->mx);
2345             scp->creator = userp;               /* remember who created it */
2346             if (!cm_HaveCallback(scp)) {
2347                 cm_MergeStatus(scp, &newFileStatus, &volSync,
2348                                 userp, 0);
2349                 cm_EndCallbackGrantingCall(scp, &cbReq,
2350                                             &newFileCallback, 0);
2351                 didEnd = 1;     
2352             }       
2353             lock_ReleaseMutex(&scp->mx);
2354             *scpp = scp;
2355         }
2356     }
2357
2358     /* make sure we end things properly */
2359     if (!didEnd)
2360         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2361
2362     return code;
2363 }       
2364
2365 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2366 {
2367     long code;
2368
2369     lock_ObtainWrite(&scp->bufCreateLock);
2370     code = buf_CleanVnode(scp, userp, reqp);
2371     lock_ReleaseWrite(&scp->bufCreateLock);
2372     if (code == 0) {
2373         lock_ObtainMutex(&scp->mx);
2374
2375         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2376                           | CM_SCACHEMASK_CLIENTMODTIME
2377                           | CM_SCACHEMASK_LENGTH))
2378             code = cm_StoreMini(scp, userp, reqp);
2379
2380         if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2381             code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2382             scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2383         }
2384
2385         lock_ReleaseMutex(&scp->mx);
2386     }
2387     return code;
2388 }
2389
2390 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2391                  cm_user_t *userp, cm_req_t *reqp)
2392 {
2393     cm_conn_t *connp;
2394     long code;
2395     AFSFid dirAFSFid;
2396     cm_callbackRequest_t cbReq;
2397     AFSFid newAFSFid;
2398     cm_fid_t newFid;
2399     cm_scache_t *scp;
2400     int didEnd;
2401     AFSStoreStatus inStatus;
2402     AFSFetchStatus updatedDirStatus;
2403     AFSFetchStatus newDirStatus;
2404     AFSCallBack newDirCallback;
2405     AFSVolSync volSync;
2406     struct rx_connection * callp;
2407
2408     /* can't create names with @sys in them; must expand it manually first.
2409      * return "invalid request" if they try.
2410      */
2411     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2412         return CM_ERROR_ATSYS;
2413     }
2414
2415     /* before starting the RPC, mark that we're changing the directory
2416      * data, so that someone who does a chmod on the dir will wait until
2417      * our call completes.
2418      */
2419     lock_ObtainMutex(&dscp->mx);
2420     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2421     if (code == 0) {
2422         cm_StartCallbackGrantingCall(NULL, &cbReq);
2423     }
2424     lock_ReleaseMutex(&dscp->mx);
2425     if (code) {
2426         return code;
2427     }
2428     didEnd = 0;
2429
2430     cm_StatusFromAttr(&inStatus, NULL, attrp);
2431
2432     /* try the RPC now */
2433     osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2434     do {
2435         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2436         if (code) 
2437             continue;
2438
2439         dirAFSFid.Volume = dscp->fid.volume;
2440         dirAFSFid.Vnode = dscp->fid.vnode;
2441         dirAFSFid.Unique = dscp->fid.unique;
2442
2443         callp = cm_GetRxConn(connp);
2444         code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2445                               &inStatus, &newAFSFid, &newDirStatus,
2446                               &updatedDirStatus, &newDirCallback,
2447                               &volSync);
2448         rx_PutConnection(callp);
2449
2450     } while (cm_Analyze(connp, userp, reqp,
2451                          &dscp->fid, &volSync, NULL, &cbReq, code));
2452     code = cm_MapRPCError(code, reqp);
2453         
2454     if (code)
2455         osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2456     else
2457         osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2458
2459     lock_ObtainMutex(&dscp->mx);
2460     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2461     if (code == 0) {
2462         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2463     }
2464     lock_ReleaseMutex(&dscp->mx);
2465
2466     /* now try to create the new dir's entry, too, but be careful to 
2467      * make sure that we don't merge in old info.  Since we weren't locking
2468      * out any requests during the file's creation, we may have pretty old
2469      * info.
2470      */
2471     if (code == 0) {
2472         newFid.cell = dscp->fid.cell;
2473         newFid.volume = dscp->fid.volume;
2474         newFid.vnode = newAFSFid.Vnode;
2475         newFid.unique = newAFSFid.Unique;
2476         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2477         if (code == 0) {
2478             lock_ObtainMutex(&scp->mx);
2479             if (!cm_HaveCallback(scp)) {
2480                 cm_MergeStatus(scp, &newDirStatus, &volSync,
2481                                 userp, 0);
2482                 cm_EndCallbackGrantingCall(scp, &cbReq,
2483                                             &newDirCallback, 0);
2484                 didEnd = 1;             
2485             }
2486             lock_ReleaseMutex(&scp->mx);
2487             cm_ReleaseSCache(scp);
2488         }
2489     }
2490
2491     /* make sure we end things properly */
2492     if (!didEnd)
2493         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2494
2495     /* and return error code */
2496     return code;
2497 }       
2498
2499 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2500              cm_user_t *userp, cm_req_t *reqp)
2501 {
2502     cm_conn_t *connp;
2503     long code = 0;
2504     AFSFid dirAFSFid;
2505     AFSFid existingAFSFid;
2506     AFSFetchStatus updatedDirStatus;
2507     AFSFetchStatus newLinkStatus;
2508     AFSVolSync volSync;
2509     struct rx_connection * callp;
2510
2511     if (dscp->fid.cell != sscp->fid.cell ||
2512         dscp->fid.volume != sscp->fid.volume) {
2513         return CM_ERROR_CROSSDEVLINK;
2514     }
2515
2516     lock_ObtainMutex(&dscp->mx);
2517     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2518     lock_ReleaseMutex(&dscp->mx);
2519
2520     if (code)
2521         return code;
2522
2523     /* try the RPC now */
2524     osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2525     do {
2526         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2527         if (code) continue;
2528
2529         dirAFSFid.Volume = dscp->fid.volume;
2530         dirAFSFid.Vnode = dscp->fid.vnode;
2531         dirAFSFid.Unique = dscp->fid.unique;
2532
2533         existingAFSFid.Volume = sscp->fid.volume;
2534         existingAFSFid.Vnode = sscp->fid.vnode;
2535         existingAFSFid.Unique = sscp->fid.unique;
2536
2537         callp = cm_GetRxConn(connp);
2538         code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2539             &newLinkStatus, &updatedDirStatus, &volSync);
2540         rx_PutConnection(callp);
2541         osi_Log1(smb_logp,"  RXAFS_Link returns 0x%x", code);
2542
2543     } while (cm_Analyze(connp, userp, reqp,
2544         &dscp->fid, &volSync, NULL, NULL, code));
2545
2546     code = cm_MapRPCError(code, reqp);
2547
2548     if (code)
2549         osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2550     else
2551         osi_Log0(afsd_logp, "CALL Link SUCCESS");
2552
2553     lock_ObtainMutex(&dscp->mx);
2554     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2555     if (code == 0) {
2556         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2557     }
2558     lock_ReleaseMutex(&dscp->mx);
2559
2560     return code;
2561 }
2562
2563 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2564                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2565 {
2566     cm_conn_t *connp;
2567     long code;
2568     AFSFid dirAFSFid;
2569     AFSFid newAFSFid;
2570     cm_fid_t newFid;
2571     cm_scache_t *scp;
2572     AFSStoreStatus inStatus;
2573     AFSFetchStatus updatedDirStatus;
2574     AFSFetchStatus newLinkStatus;
2575     AFSVolSync volSync;
2576     struct rx_connection * callp;
2577
2578     /* before starting the RPC, mark that we're changing the directory data,
2579      * so that someone who does a chmod on the dir will wait until our
2580      * call completes.
2581      */
2582     lock_ObtainMutex(&dscp->mx);
2583     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2584     lock_ReleaseMutex(&dscp->mx);
2585     if (code) {
2586         return code;
2587     }
2588
2589     cm_StatusFromAttr(&inStatus, NULL, attrp);
2590
2591     /* try the RPC now */
2592     osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
2593     do {
2594         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2595         if (code) 
2596             continue;
2597
2598         dirAFSFid.Volume = dscp->fid.volume;
2599         dirAFSFid.Vnode = dscp->fid.vnode;
2600         dirAFSFid.Unique = dscp->fid.unique;
2601
2602         callp = cm_GetRxConn(connp);
2603         code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2604                               &inStatus, &newAFSFid, &newLinkStatus,
2605                               &updatedDirStatus, &volSync);
2606         rx_PutConnection(callp);
2607
2608     } while (cm_Analyze(connp, userp, reqp,
2609                          &dscp->fid, &volSync, NULL, NULL, code));
2610     code = cm_MapRPCError(code, reqp);
2611         
2612     if (code)
2613         osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
2614     else
2615         osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
2616
2617     lock_ObtainMutex(&dscp->mx);
2618     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2619     if (code == 0) {
2620         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2621     }
2622     lock_ReleaseMutex(&dscp->mx);
2623
2624     /* now try to create the new dir's entry, too, but be careful to 
2625      * make sure that we don't merge in old info.  Since we weren't locking
2626      * out any requests during the file's creation, we may have pretty old
2627      * info.
2628      */
2629     if (code == 0) {
2630         newFid.cell = dscp->fid.cell;
2631         newFid.volume = dscp->fid.volume;
2632         newFid.vnode = newAFSFid.Vnode;
2633         newFid.unique = newAFSFid.Unique;
2634         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2635         if (code == 0) {
2636             lock_ObtainMutex(&scp->mx);
2637             if (!cm_HaveCallback(scp)) {
2638                 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2639                                 userp, 0);
2640             }       
2641             lock_ReleaseMutex(&scp->mx);
2642             cm_ReleaseSCache(scp);
2643         }
2644     }
2645         
2646     /* and return error code */
2647     return code;
2648 }
2649
2650 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2651                    cm_req_t *reqp)
2652 {
2653     cm_conn_t *connp;
2654     long code;
2655     AFSFid dirAFSFid;
2656     int didEnd;
2657     AFSFetchStatus updatedDirStatus;
2658     AFSVolSync volSync;
2659     struct rx_connection * callp;
2660
2661     /* before starting the RPC, mark that we're changing the directory data,
2662      * so that someone who does a chmod on the dir will wait until our
2663      * call completes.
2664      */
2665     lock_ObtainMutex(&dscp->mx);
2666     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2667     lock_ReleaseMutex(&dscp->mx);
2668     if (code) {
2669         return code;
2670     }
2671     didEnd = 0;
2672
2673     /* try the RPC now */
2674     osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
2675     do {
2676         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2677         if (code) 
2678             continue;
2679
2680         dirAFSFid.Volume = dscp->fid.volume;
2681         dirAFSFid.Vnode = dscp->fid.vnode;
2682         dirAFSFid.Unique = dscp->fid.unique;
2683
2684         callp = cm_GetRxConn(connp);
2685         code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2686                                 &updatedDirStatus, &volSync);
2687         rx_PutConnection(callp);
2688
2689     } while (cm_Analyze(connp, userp, reqp,
2690                          &dscp->fid, &volSync, NULL, NULL, code));
2691     code = cm_MapRPCErrorRmdir(code, reqp);
2692
2693     if (code)
2694         osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
2695     else
2696         osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
2697
2698     lock_ObtainMutex(&dscp->mx);
2699     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2700     if (code == 0) {
2701         cm_dnlcRemove(dscp, namep); 
2702         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2703     }
2704     lock_ReleaseMutex(&dscp->mx);
2705
2706     /* and return error code */
2707     return code;
2708 }
2709
2710 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2711 {
2712     /* grab mutex on contents */
2713     lock_ObtainMutex(&scp->mx);
2714
2715     /* reset the prefetch info */
2716     scp->prefetch.base.LowPart = 0;             /* base */
2717     scp->prefetch.base.HighPart = 0;
2718     scp->prefetch.end.LowPart = 0;              /* and end */
2719     scp->prefetch.end.HighPart = 0;
2720
2721     /* release mutex on contents */
2722     lock_ReleaseMutex(&scp->mx);
2723
2724     /* we're done */
2725     return 0;
2726 }       
2727
2728 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2729                 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2730 {
2731     cm_conn_t *connp;
2732     long code;
2733     AFSFid oldDirAFSFid;
2734     AFSFid newDirAFSFid;
2735     int didEnd;
2736     AFSFetchStatus updatedOldDirStatus;
2737     AFSFetchStatus updatedNewDirStatus;
2738     AFSVolSync volSync;
2739     int oneDir;
2740     struct rx_connection * callp;
2741
2742     /* before starting the RPC, mark that we're changing the directory data,
2743      * so that someone who does a chmod on the dir will wait until our call
2744      * completes.  We do this in vnode order so that we don't deadlock,
2745      * which makes the code a little verbose.
2746      */
2747     if (oldDscp == newDscp) {
2748         /* check for identical names */
2749         if (strcmp(oldNamep, newNamep) == 0)
2750             return CM_ERROR_RENAME_IDENTICAL;
2751
2752         oneDir = 1;
2753         lock_ObtainMutex(&oldDscp->mx);
2754         cm_dnlcRemove(oldDscp, oldNamep);
2755         cm_dnlcRemove(oldDscp, newNamep);
2756         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2757                           CM_SCACHESYNC_STOREDATA);
2758         lock_ReleaseMutex(&oldDscp->mx);
2759     }
2760     else {
2761         /* two distinct dir vnodes */
2762         oneDir = 0;
2763         if (oldDscp->fid.cell != newDscp->fid.cell ||
2764              oldDscp->fid.volume != newDscp->fid.volume)
2765             return CM_ERROR_CROSSDEVLINK;
2766
2767         /* shouldn't happen that we have distinct vnodes for two
2768          * different files, but could due to deliberate attack, or
2769          * stale info.  Avoid deadlocks and quit now.
2770          */
2771         if (oldDscp->fid.vnode == newDscp->fid.vnode)
2772             return CM_ERROR_CROSSDEVLINK;
2773
2774         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2775             lock_ObtainMutex(&oldDscp->mx);
2776             cm_dnlcRemove(oldDscp, oldNamep);
2777             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2778                               CM_SCACHESYNC_STOREDATA);
2779             lock_ReleaseMutex(&oldDscp->mx);
2780             if (code == 0) {
2781                 lock_ObtainMutex(&newDscp->mx);
2782                 cm_dnlcRemove(newDscp, newNamep);
2783                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2784                                   CM_SCACHESYNC_STOREDATA);
2785                 lock_ReleaseMutex(&newDscp->mx);
2786                 if (code) {
2787                     /* cleanup first one */
2788                     lock_ObtainMutex(&newDscp->mx);
2789                     cm_SyncOpDone(oldDscp, NULL,
2790                                    CM_SCACHESYNC_STOREDATA);
2791                     lock_ReleaseMutex(&oldDscp->mx);
2792                 }       
2793             }
2794         }
2795         else {
2796             /* lock the new vnode entry first */
2797             lock_ObtainMutex(&newDscp->mx);
2798             cm_dnlcRemove(newDscp, newNamep);
2799             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2800                               CM_SCACHESYNC_STOREDATA);
2801             lock_ReleaseMutex(&newDscp->mx);
2802             if (code == 0) {
2803                 lock_ObtainMutex(&oldDscp->mx);
2804                 cm_dnlcRemove(oldDscp, oldNamep);
2805                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2806                                   CM_SCACHESYNC_STOREDATA);
2807                 lock_ReleaseMutex(&oldDscp->mx);
2808                 if (code) {
2809                     /* cleanup first one */
2810                     lock_ObtainMutex(&newDscp->mx);
2811                     cm_SyncOpDone(newDscp, NULL,
2812                                    CM_SCACHESYNC_STOREDATA);
2813                     lock_ReleaseMutex(&newDscp->mx);
2814                 }       
2815             }
2816         }
2817     }   /* two distinct vnodes */
2818
2819     if (code) {
2820         return code;
2821     }
2822     didEnd = 0;
2823
2824     /* try the RPC now */
2825     osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p", 
2826               oldDscp, newDscp);
2827     do {
2828         code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2829         if (code) 
2830             continue;
2831
2832         oldDirAFSFid.Volume = oldDscp->fid.volume;
2833         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2834         oldDirAFSFid.Unique = oldDscp->fid.unique;
2835         newDirAFSFid.Volume = newDscp->fid.volume;
2836         newDirAFSFid.Vnode = newDscp->fid.vnode;
2837         newDirAFSFid.Unique = newDscp->fid.unique;
2838
2839         callp = cm_GetRxConn(connp);
2840         code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2841                              &newDirAFSFid, newNamep,
2842                              &updatedOldDirStatus, &updatedNewDirStatus,
2843                              &volSync);
2844         rx_PutConnection(callp);
2845
2846     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2847                          &volSync, NULL, NULL, code));
2848     code = cm_MapRPCError(code, reqp);
2849         
2850     if (code)
2851         osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2852     else
2853         osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2854
2855     /* update the individual stat cache entries for the directories */
2856     lock_ObtainMutex(&oldDscp->mx);
2857     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2858     if (code == 0) {
2859         cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2860                         userp, 0);
2861     }
2862     lock_ReleaseMutex(&oldDscp->mx);
2863
2864     /* and update it for the new one, too, if necessary */
2865     if (!oneDir) {
2866         lock_ObtainMutex(&newDscp->mx);
2867         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2868         if (code == 0) {
2869             cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2870                             userp, 0);
2871         }
2872         lock_ReleaseMutex(&newDscp->mx);
2873     }
2874
2875     /* and return error code */
2876     return code;
2877 }
2878
2879 /* Byte range locks:
2880
2881    The OpenAFS Windows client has to fake byte range locks given no
2882    server side support for such locks.  This is implemented as keyed
2883    byte range locks on the cache manager.
2884
2885    Keyed byte range locks:
2886
2887    Each cm_scache_t structure keeps track of a list of keyed locks.
2888    The key for a lock identifies an owner of a set of locks (referred
2889    to as a client).  Each key is represented by a value.  The set of
2890    key values used within a specific cm_scache_t structure form a
2891    namespace that has a scope of just that cm_scache_t structure.  The
2892    same key value can be used with another cm_scache_t structure and
2893    correspond to a completely different client.  However it is
2894    advantageous for the SMB or IFS layer to make sure that there is a
2895    1-1 mapping between client and keys over all cm_scache_t objects.
2896
2897    Assume a client C has key Key(C) (although, since the scope of the
2898    key is a cm_scache_t, the key can be Key(C,S), where S is the
2899    cm_scache_t.  But assume a 1-1 relation between keys and clients).
2900    A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
2901    inclusive (a.k.a. [O,O+L-1]).  The function Key(x) is implemented
2902    through cm_generateKey() function for both SMB and IFS.
2903
2904    The list of locks for a cm_scache_t object S is maintained in
2905    S->fileLocks.  The cache manager will set a lock on the AFS file
2906    server in order to assert the locks in S->fileLocks.  If only
2907    shared locks are in place for S, then the cache manager will obtain
2908    a LockRead lock, while if there are any exclusive locks, it will
2909    obtain a LockWrite lock.  If the exclusive locks are all released
2910    while the shared locks remain, then the cache manager will
2911    downgrade the lock from LockWrite to LockRead.  Similarly, if an
2912    exclusive lock is obtained when only shared locks exist, then the
2913    cache manager will try to upgrade the lock from LockRead to
2914    LockWrite.
2915
2916    Each lock L owned by client C maintains a key L->key such that
2917    L->key == Key(C), the effective range defined by L->LOffset and
2918    L->LLength such that the range of bytes affected by the lock is
2919    (L->LOffset, +L->LLength), a type maintained in L->LockType which
2920    is either exclusive or shared.
2921
2922    Lock states:
2923
2924    A lock exists iff it is in S->fileLocks for some cm_scache_t
2925    S. Existing locks are in one of the following states: ACTIVE,
2926    WAITLOCK, WAITUNLOCK, LOST, DELETED.
2927
2928    The following sections describe each lock and the associated
2929    transitions.
2930
2931    1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
2932       the lock with the AFS file server.  This type of lock can be
2933       exercised by a client to read or write to the locked region (as
2934       the lock allows).
2935
2936       1.1 ACTIVE->LOST: When the AFS file server fails to extend a
2937         server lock that was required to assert the lock.  Before
2938         marking the lock as lost, the cache manager checks if the file
2939         has changed on the server.  If the file has not changed, then
2940         the cache manager will attempt to obtain a new server lock
2941         that is sufficient to assert the client side locks for the
2942         file.  If any of these fail, the lock is marked as LOST.
2943         Otherwise, it is left as ACTIVE.
2944
2945       1.2 ACTIVE->DELETED: Lock is released.
2946
2947    2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
2948       grants the lock but the lock is yet to be asserted with the AFS
2949       file server.  Once the file server grants the lock, the state
2950       will transition to an ACTIVE lock.
2951
2952       2.1 WAITLOCK->ACTIVE: The server granted the lock.
2953
2954       2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
2955         waiting.
2956
2957       2.3 WAITLOCK->LOST: One or more locks from this client were
2958         marked as LOST.  No further locks will be granted to this
2959         client until all lost locks are removed.
2960
2961    3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
2962       receives a request for a lock that conflicts with an existing
2963       ACTIVE or WAITLOCK lock.  The lock will be placed in the queue
2964       and will be granted at such time the conflicting locks are
2965       removed, at which point the state will transition to either
2966       WAITLOCK or ACTIVE.
2967
2968       3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed.  The
2969         current serverLock is sufficient to assert this lock, or a
2970         sufficient serverLock is obtained.
2971
2972       3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
2973         however the required serverLock is yet to be asserted with the
2974         server.
2975
2976       3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
2977         released.
2978
2979       3.5 WAITUNLOCK->LOST: One or more locks from this client were
2980         marked as LOST.  No further locks will be granted to this
2981         client until all lost locks are removed.
2982
2983    4. LOST: A lock L is LOST if the server lock that was required to
2984       assert the lock could not be obtained or if it could not be
2985       extended, or if other locks by the same client were LOST.
2986       Essentially, once a lock is LOST, the contract between the cache
2987       manager and that specific client is no longer valid.
2988
2989       The cache manager rechecks the server lock once every minute and
2990       extends it as appropriate.  If this is not done for 5 minutes,
2991       the AFS file server will release the lock (the 5 minute timeout
2992       is based on current file server code and is fairly arbitrary).
2993       Once released, the lock cannot be re-obtained without verifying
2994       that the contents of the file hasn't been modified since the
2995       time the lock was released.  Re-obtaining the lock without
2996       verifying this may lead to data corruption.  If the lock can not
2997       be obtained safely, then all active locks for the cm_scache_t
2998       are marked as LOST.
2999
3000       4.1 LOST->DELETED: The lock is released.
3001
3002    5. DELETED: The lock is no longer relevant.  Eventually, it will
3003       get removed from the cm_scache_t. In the meantime, it will be
3004       treated as if it does not exist.
3005
3006       5.1 DELETED->not exist: The lock is removed from the
3007         cm_scache_t.
3008
3009    The following are classifications of locks based on their state.
3010
3011    6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK.  These locks
3012       have been accepted by the cache manager, but may or may not have
3013       been granted back to the client.
3014
3015    7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3016
3017    8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3018
3019    Lock operation:
3020
3021    A client C can READ range (Offset,+Length) of a file represented by
3022    cm_scache_t S iff (1):
3023
3024    1. for all _a_ in (Offset,+Length), all of the following is true:
3025
3026        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3027          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3028          shared.
3029
3030        1.2 For each LOST lock L in S->fileLocks such that _a_ in
3031          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3032          Key(C)
3033
3034        (When locks are lost on an cm_scache_t, all locks are lost.  By
3035        4.2 (below), if there is an exclusive LOST lock, then there
3036        can't be any overlapping ACTIVE locks.)
3037
3038    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3039
3040    2. for all _a_ in (Offset,+Length), one of the following is true:
3041
3042        2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3043          does not exist a LOST lock L such that _a_ in
3044          (L->LOffset,+L->LLength).
3045
3046        2.2 Byte _a_ of S is owned by C under lock L (as specified in
3047          1.2) AND L->LockType is exclusive.
3048
3049    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3050
3051    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3052       true:
3053
3054        3.1 If L->LockType is exclusive then there does NOT exist a
3055          ACCEPTED lock M in S->fileLocks such that _a_ in
3056          (M->LOffset,+M->LLength).
3057
3058          (If we count all QUEUED locks then we hit cases such as
3059          cascading waiting locks where the locks later on in the queue
3060          can be granted without compromising file integrity.  On the
3061          other hand if only ACCEPTED locks are considered, then locks
3062          that were received earlier may end up waiting for locks that
3063          were received later to be unlocked. The choice of ACCEPTED
3064          locks was made to mimic the Windows byte range lock
3065          semantics.)
3066
3067        3.2 If L->LockType is shared then for each ACCEPTED lock M in
3068          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3069          M->LockType is shared.
3070
3071    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3072
3073        4.1 M->key != Key(C)
3074
3075        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3076          and (M->LOffset,+M->LLength) do not intersect.
3077
3078          (Note: If a client loses a lock, it loses all locks.
3079          Subsequently, it will not be allowed to obtain any more locks
3080          until all existing LOST locks that belong to the client are
3081          released.  Once all locks are released by a single client,
3082          there exists no further contract between the client and AFS
3083          about the contents of the file, hence the client can then
3084          proceed to obtain new locks and establish a new contract.
3085
3086          This doesn't quite work as you think it should, because most
3087          applications aren't built to deal with losing locks they
3088          thought they once had.  For now, we don't have a good
3089          solution to lost locks.
3090
3091          Also, for consistency reasons, we have to hold off on
3092          granting locks that overlap exclusive LOST locks.)
3093
3094    A client C can only unlock locks L in S->fileLocks which have
3095    L->key == Key(C).
3096
3097    The representation and invariants are as follows:
3098
3099    - Each cm_scache_t structure keeps:
3100
3101        - A queue of byte-range locks (cm_scache_t::fileLocks) which
3102          are of type cm_file_lock_t.
3103
3104        - A record of the highest server-side lock that has been
3105          obtained for this object (cm_scache_t::serverLock), which is
3106          one of (-1), LockRead, LockWrite.
3107
3108        - A count of ACCEPTED exclusive and shared locks that are in the
3109          queue (cm_scache_t::sharedLocks and
3110          cm_scache_t::exclusiveLocks)
3111
3112    - Each cm_file_lock_t structure keeps:
3113
3114        - The type of lock (cm_file_lock_t::LockType)
3115
3116        - The key associated with the lock (cm_file_lock_t::key)
3117
3118        - The offset and length of the lock (cm_file_lock_t::LOffset
3119          and cm_file_lock_t::LLength)
3120
3121        - The state of the lock.
3122
3123        - Time of issuance or last successful extension
3124
3125    Semantic invariants:
3126
3127        I1. The number of ACCEPTED locks in S->fileLocks are
3128            (S->sharedLocks + S->exclusiveLocks)
3129
3130    External invariants:
3131
3132        I3. S->serverLock is the lock that we have asserted with the
3133            AFS file server for this cm_scache_t.
3134
3135        I4. S->serverLock == LockRead iff there is at least one ACTIVE
3136            shared lock, but no ACTIVE exclusive locks.
3137
3138        I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3139            exclusive lock.
3140
3141        I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3142            M->key == L->key IMPLIES M is LOST or DELETED.
3143
3144    --asanka
3145  */
3146
3147 #define IS_LOCK_ACTIVE(lockp)     (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3148
3149 #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)
3150
3151 #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)
3152
3153 #define IS_LOCK_LOST(lockp)       (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3154
3155 #define IS_LOCK_DELETED(lockp)    (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3156
3157 /* unsafe */
3158 #define IS_LOCK_ACCEPTED(lockp)   (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3159
3160 /* unsafe */
3161 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3162
3163 /* unsafe */
3164 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3165
3166 /* unsafe */
3167 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3168
3169 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3170 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3171 #else
3172 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3173 #endif
3174
3175 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3176
3177 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3178 {
3179     afs_int64 int_begin;
3180     afs_int64 int_end;
3181
3182     int_begin = MAX(pos->offset, neg->offset);
3183     int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3184
3185     if (int_begin < int_end) {
3186         if (int_begin == pos->offset) {
3187             pos->length = pos->offset + pos->length - int_end;
3188             pos->offset = int_end;
3189         } else if (int_end == pos->offset + pos->length) {
3190             pos->length = int_begin - pos->offset;
3191         }
3192
3193         /* We only subtract ranges if the resulting range is
3194            contiguous.  If we try to support non-contigous ranges, we
3195            aren't actually improving performance. */
3196     }
3197 }
3198
3199 /* Called with scp->mx held.  Returns 0 if all is clear to read the
3200    specified range by the client identified by key.
3201  */
3202 long cm_LockCheckRead(cm_scache_t *scp, 
3203                       LARGE_INTEGER LOffset, 
3204                       LARGE_INTEGER LLength, 
3205                       cm_key_t key)
3206 {
3207 #ifndef ADVISORY_LOCKS
3208
3209     cm_file_lock_t *fileLock;
3210     osi_queue_t *q;
3211     long code = 0;
3212     cm_range_t range;
3213     int substract_ranges = FALSE;
3214
3215     range.offset = LOffset.QuadPart;
3216     range.length = LLength.QuadPart;
3217
3218     /*
3219
3220      1. for all _a_ in (Offset,+Length), all of the following is true:
3221
3222        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3223          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3224          shared.
3225
3226        1.2 For each LOST lock L in S->fileLocks such that _a_ in
3227          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3228          Key(C)
3229
3230     */
3231
3232     lock_ObtainRead(&cm_scacheLock);
3233
3234     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3235         fileLock = 
3236             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3237
3238         if (INTERSECT_RANGE(range, fileLock->range)) {
3239             if (IS_LOCK_ACTIVE(fileLock)) {
3240                 if (fileLock->key == key) {
3241
3242                     /* If there is an active lock for this client, it
3243                        is safe to substract ranges.*/
3244                     cm_LockRangeSubtract(&range, &fileLock->range);
3245                     substract_ranges = TRUE;
3246                 } else {
3247                     if (fileLock->lockType != LockRead) {
3248                         code = CM_ERROR_LOCK_CONFLICT;
3249                         break;
3250                     }
3251
3252                     /* even if the entire range is locked for reading,
3253                        we still can't grant the lock at this point
3254                        because the client may have lost locks. That
3255                        is, unless we have already seen an active lock
3256                        belonging to the client, in which case there
3257                        can't be any lost locks for this client. */
3258                     if (substract_ranges)
3259                         cm_LockRangeSubtract(&range, &fileLock->range);
3260                 }
3261             } else if (IS_LOCK_LOST(fileLock) &&
3262                       (fileLock->key == key || fileLock->lockType == LockWrite)) {
3263                 code = CM_ERROR_BADFD;
3264                 break;
3265             }
3266         }
3267     }
3268
3269     lock_ReleaseRead(&cm_scacheLock);
3270
3271     osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3272               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3273
3274     return code;
3275
3276 #else
3277
3278     return 0;
3279
3280 #endif
3281 }
3282
3283 /* Called with scp->mx held.  Returns 0 if all is clear to write the
3284    specified range by the client identified by key.
3285  */
3286 long cm_LockCheckWrite(cm_scache_t *scp,
3287                        LARGE_INTEGER LOffset,
3288                        LARGE_INTEGER LLength,
3289                        cm_key_t key)
3290 {
3291 #ifndef ADVISORY_LOCKS
3292
3293     cm_file_lock_t *fileLock;
3294     osi_queue_t *q;
3295     long code = 0;
3296     cm_range_t range;
3297
3298     range.offset = LOffset.QuadPart;
3299     range.length = LLength.QuadPart;
3300
3301     /*
3302    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3303
3304    2. for all _a_ in (Offset,+Length), one of the following is true:
3305
3306        2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3307          lock L such that _a_ in (L->LOffset,+L->LLength).
3308
3309        2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3310          exclusive.
3311     */
3312
3313     lock_ObtainRead(&cm_scacheLock);
3314
3315     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3316         fileLock = 
3317             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3318
3319         if (INTERSECT_RANGE(range, fileLock->range)) {
3320             if (IS_LOCK_ACTIVE(fileLock)) {
3321                 if (fileLock->key == key) {
3322                     if (fileLock->lockType == LockWrite) {
3323
3324                         /* if there is an active lock for this client, it
3325                            is safe to substract ranges */
3326                         cm_LockRangeSubtract(&range, &fileLock->range);
3327                     } else {
3328                         code = CM_ERROR_LOCK_CONFLICT;
3329                         break;
3330                     }
3331                 } else {
3332                     code = CM_ERROR_LOCK_CONFLICT;
3333                     break;
3334                 }
3335             } else if (IS_LOCK_LOST(fileLock)) {
3336                 code = CM_ERROR_BADFD;
3337                 break;
3338             }
3339         }
3340     }
3341
3342     lock_ReleaseRead(&cm_scacheLock);
3343
3344     osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3345               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3346
3347     return code;
3348
3349 #else
3350
3351     return 0;
3352
3353 #endif
3354 }
3355
3356 /* Forward dcl. */
3357 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3358
3359 /* Called with cm_scacheLock write locked */
3360 static cm_file_lock_t * cm_GetFileLock(void) {
3361     cm_file_lock_t * l;
3362
3363     l = (cm_file_lock_t *) cm_freeFileLocks;
3364     if (l) {
3365         osi_QRemove(&cm_freeFileLocks, &l->q);
3366     } else {
3367         l = malloc(sizeof(cm_file_lock_t));
3368         osi_assert(l);
3369     }
3370
3371     memset(l, 0, sizeof(cm_file_lock_t));
3372
3373     return l;
3374 }
3375
3376 /* Called with cm_scacheLock write locked */
3377 static void cm_PutFileLock(cm_file_lock_t *l) {
3378     osi_QAdd(&cm_freeFileLocks, &l->q);
3379 }
3380
3381 /* called with scp->mx held.  May release it during processing, but
3382    leaves it held on exit. */
3383 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
3384                    cm_req_t * reqp) {
3385     long code = 0;
3386     AFSFid tfid;
3387     cm_fid_t cfid;
3388     cm_conn_t * connp;
3389     struct rx_connection * callp;
3390     AFSVolSync volSync;
3391
3392     tfid.Volume = scp->fid.volume;
3393     tfid.Vnode = scp->fid.vnode;
3394     tfid.Unique = scp->fid.unique;
3395     cfid = scp->fid;
3396
3397     osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
3398
3399     lock_ReleaseMutex(&scp->mx);
3400
3401     do {
3402         code = cm_Conn(&cfid, userp, reqp, &connp);
3403         if (code) 
3404             break;
3405
3406         callp = cm_GetRxConn(connp);
3407         code = RXAFS_SetLock(callp, &tfid, lockType,
3408                              &volSync);
3409         rx_PutConnection(callp);
3410
3411     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3412                         NULL, NULL, code));
3413
3414     code = cm_MapRPCError(code, reqp);
3415     if (code) {
3416         osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3417     } else {
3418         osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3419     }
3420
3421     lock_ObtainMutex(&scp->mx);
3422
3423     return code;
3424 }
3425
3426 /* called with scp->mx held.  Releases it during processing */
3427 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
3428                        cm_req_t * reqp) {
3429     long code = 0;
3430     AFSFid tfid;
3431     cm_fid_t cfid;
3432     cm_conn_t * connp;
3433     struct rx_connection * callp;
3434     AFSVolSync volSync;
3435
3436     tfid.Volume = scp->fid.volume;
3437     tfid.Vnode = scp->fid.vnode;
3438     tfid.Unique = scp->fid.unique;
3439     cfid = scp->fid;
3440
3441     lock_ReleaseMutex(&scp->mx);
3442
3443     osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3444
3445     do {
3446         code = cm_Conn(&cfid, userp, reqp, &connp);
3447         if (code) 
3448             break;
3449
3450         callp = cm_GetRxConn(connp);
3451         code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3452         rx_PutConnection(callp);
3453
3454     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3455                         NULL, NULL, code));
3456     code = cm_MapRPCError(code, reqp);
3457     if (code)
3458         osi_Log1(afsd_logp,
3459                  "CALL ReleaseLock FAILURE, code 0x%x", code);
3460     else
3461         osi_Log0(afsd_logp,
3462                  "CALL ReleaseLock SUCCESS");
3463         
3464     lock_ObtainMutex(&scp->mx);
3465
3466     return code;
3467 }
3468
3469 /* called with scp->mx held.  May release it during processing, but
3470    will exit with lock held.
3471
3472    This will return:
3473
3474    - 0 if the user has permission to get the specified lock for the scp
3475
3476    - CM_ERROR_NOACCESS if not
3477
3478    Any other error from cm_SyncOp will be sent down untranslated.
3479 */
3480 long cm_LockCheckPerms(cm_scache_t * scp,
3481                        int lock_type,
3482                        cm_user_t * userp,
3483                        cm_req_t * reqp)
3484 {
3485     long rights = 0;
3486     long code = 0;
3487
3488     /* lock permissions are slightly tricky because of the 'i' bit.
3489        If the user has PRSFS_LOCK, she can read-lock the file.  If the
3490        user has PRSFS_WRITE, she can write-lock the file.  However, if
3491        the user has PRSFS_INSERT, then she can write-lock new files,
3492        but not old ones.  Since we don't have information about
3493        whether a file is new or not, we assume that if the user owns
3494        the scp, then she has the permissions that are granted by
3495        PRSFS_INSERT. */
3496
3497     osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
3498              scp, lock_type, userp);
3499
3500     if (lock_type == LockRead)
3501         rights |= PRSFS_LOCK;
3502     else if (lock_type == LockWrite)
3503         rights |= PRSFS_WRITE;
3504     else {
3505         /* hmmkay */
3506         osi_assert(FALSE);
3507         return 0;
3508     }
3509
3510     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
3511                      CM_SCACHESYNC_GETSTATUS |
3512                      CM_SCACHESYNC_NEEDCALLBACK);
3513
3514     if (code == CM_ERROR_NOACCESS &&
3515         lock_type == LockWrite &&
3516         scp->creator == userp) {
3517         /* check for PRSFS_INSERT. */
3518
3519         code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
3520                          CM_SCACHESYNC_GETSTATUS |
3521                          CM_SCACHESYNC_NEEDCALLBACK);
3522
3523         if (code == CM_ERROR_NOACCESS)
3524             osi_Log0(afsd_logp, "cm_LockCheckPerms user is creator but has no INSERT bits for scp");
3525     }
3526
3527     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
3528
3529     osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
3530
3531     return code;
3532 }
3533
3534 /* called with scp->mx held */
3535 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
3536              LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
3537              cm_key_t key,
3538              int allowWait, cm_user_t *userp, cm_req_t *reqp,
3539              cm_file_lock_t **lockpp)
3540 {
3541     long code = 0;
3542     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
3543     cm_file_lock_t *fileLock;
3544     osi_queue_t *q;
3545     cm_range_t range;
3546     int wait_unlock = FALSE;
3547     int force_client_lock = FALSE;
3548
3549     osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
3550              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
3551     osi_Log3(afsd_logp, "... allowWait %d key 0x%x:%x", allowWait, 
3552              (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
3553
3554     /*
3555    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3556
3557    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3558       true:
3559
3560        3.1 If L->LockType is exclusive then there does NOT exist a
3561          ACCEPTED lock M in S->fileLocks such that _a_ in
3562          (M->LOffset,+M->LLength).
3563
3564        3.2 If L->LockType is shared then for each ACCEPTED lock M in
3565          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3566          M->LockType is shared.
3567
3568    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3569
3570        4.1 M->key != Key(C)
3571
3572        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3573          and (M->LOffset,+M->LLength) do not intersect.
3574     */
3575
3576     range.offset = LOffset.QuadPart;
3577     range.length = LLength.QuadPart;
3578
3579     lock_ObtainRead(&cm_scacheLock);
3580
3581     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
3582         fileLock =
3583             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3584
3585         if (IS_LOCK_LOST(fileLock)) {
3586             if (fileLock->key == key) {
3587                 code = CM_ERROR_BADFD;
3588                 break;
3589             } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
3590                 code = CM_ERROR_WOULDBLOCK;
3591                 wait_unlock = TRUE;
3592                 break;
3593             }
3594         }
3595
3596         /* we don't need to check for deleted locks here since deleted
3597            locks are dequeued from scp->fileLocks */
3598         if (IS_LOCK_ACCEPTED(fileLock) &&
3599            INTERSECT_RANGE(range, fileLock->range)) {
3600
3601             if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
3602                 fileLock->lockType != LockRead) {
3603                 wait_unlock = TRUE;
3604                 code = CM_ERROR_WOULDBLOCK;
3605                 break;
3606             }
3607         }
3608     }
3609
3610     lock_ReleaseRead(&cm_scacheLock);
3611
3612     if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
3613         if (Which == scp->serverLock ||
3614            (Which == LockRead && scp->serverLock == LockWrite)) {
3615
3616             /* we already have the lock we need */
3617             osi_Log3(afsd_logp, "   we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]", 
3618                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3619
3620             code = cm_LockCheckPerms(scp, Which, userp, reqp);
3621
3622             /* special case: if we don't have permission to read-lock
3623                the file, then we force a clientside lock.  This is to
3624                compensate for applications that obtain a read-lock for
3625                reading files off of directories that don't grant
3626                read-locks to the user. */
3627             if (code == CM_ERROR_NOACCESS && Which == LockRead) {
3628                 osi_Log0(afsd_logp, "   User has no read-lock perms. Forcing client-side lock");
3629                 force_client_lock = TRUE;
3630             }
3631
3632         } else if ((scp->exclusiveLocks > 0) ||
3633                    (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
3634
3635             /* We are already waiting for some other lock.  We should
3636                wait for the daemon to catch up instead of generating a
3637                flood of SetLock calls. */
3638             osi_Log3(afsd_logp, "   already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
3639                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3640
3641             /* see if we have permission to create the lock in the
3642                first place. */
3643             code = cm_LockCheckPerms(scp, Which, userp, reqp);
3644             if (code == 0)
3645                 code = CM_ERROR_WOULDBLOCK;
3646             else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
3647                 osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
3648                 force_client_lock = TRUE;
3649             }
3650
3651             /* leave any other codes as-is */
3652
3653         } else {
3654             int newLock;
3655             int check_data_version = FALSE;
3656
3657             /* first check if we have permission to elevate or obtain
3658                the lock. */
3659             code = cm_LockCheckPerms(scp, Which, userp, reqp);
3660             if (code) {
3661                 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
3662                     osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
3663                     force_client_lock = TRUE;
3664                 }
3665                 goto check_code;
3666             }
3667
3668             if (scp->serverLock == LockRead && Which == LockWrite) {
3669
3670                 /* We want to escalate the lock to a LockWrite.
3671                    Unfortunately that's not really possible without
3672                    letting go of the current lock.  But for now we do
3673                    it anyway. */
3674
3675                 osi_Log0(afsd_logp,
3676                          "   attempting to UPGRADE from LockRead to LockWrite.");
3677                 osi_Log1(afsd_logp,
3678                          "   dataVersion on scp: %d", scp->dataVersion);
3679
3680                 /* we assume at this point (because scp->serverLock
3681                    was valid) that we had a valid server lock. */
3682                 scp->lockDataVersion = scp->dataVersion;
3683                 check_data_version = TRUE;
3684         
3685                 code = cm_IntReleaseLock(scp, userp, reqp);
3686
3687                 if (code) {
3688                     /* We couldn't release the lock */
3689                     goto check_code;
3690                 } else {
3691                     scp->serverLock = -1;
3692                 }
3693             }
3694
3695             /* We need to obtain a server lock of type Which in order
3696                to assert this file lock */
3697 #ifndef AGGRESSIVE_LOCKS
3698             newLock = Which;
3699 #else
3700             newLock = LockWrite;
3701 #endif
3702             code = cm_IntSetLock(scp, userp, newLock, reqp);
3703
3704             if (code == CM_ERROR_WOULDBLOCK && newLock != Which) {
3705                 /* we wanted LockRead.  We tried LockWrite. Now try
3706                    LockRead again */
3707                 newLock = Which;
3708
3709                 /* am I sane? */
3710                 osi_assert(newLock == LockRead);
3711
3712                 code = cm_IntSetLock(scp, userp, newLock, reqp);
3713             }
3714
3715             if (code == 0 && check_data_version &&
3716                scp->dataVersion != scp->lockDataVersion) {
3717                 /* We lost a race.  Although we successfully obtained
3718                    a lock, someone modified the file in between.  The
3719                    locks have all been technically lost. */
3720
3721                 osi_Log0(afsd_logp,
3722                          "  Data version mismatch while upgrading lock.");
3723                 osi_Log2(afsd_logp,
3724                          "  Data versions before=%d, after=%d",
3725                          scp->lockDataVersion,
3726                          scp->dataVersion);
3727                 osi_Log1(afsd_logp,
3728                          "  Releasing stale lock for scp 0x%x", scp);
3729
3730                 code = cm_IntReleaseLock(scp, userp, reqp);
3731
3732                 scp->serverLock = -1;
3733
3734                 code = CM_ERROR_INVAL;
3735             } else if (code == 0) {
3736                 scp->serverLock = newLock;
3737                 scp->lockDataVersion = scp->dataVersion;
3738             }
3739
3740             if (code != 0 &&
3741                 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
3742                     scp->serverLock == -1) {
3743                     /* Oops. We lost the lock. */
3744                     cm_LockMarkSCacheLost(scp);
3745                 }
3746             }
3747     } else if (code == 0) {     /* server locks not enabled */
3748         osi_Log0(afsd_logp,
3749                  "  Skipping server lock for scp");
3750     }
3751
3752  check_code:
3753
3754     if (code != 0 && !force_client_lock) {
3755         /* Special case error translations
3756
3757            Applications don't expect certain errors from a
3758            LockFile/UnlockFile call.  We need to translate some error
3759            code to codes that apps expect and handle. */
3760
3761         /* We shouldn't actually need to handle this case since we
3762            simulate locks for RO scps anyway. */
3763         if (code == CM_ERROR_READONLY) {
3764             osi_Log0(afsd_logp, "   Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
3765             code = CM_ERROR_NOACCESS;
3766         }
3767     }
3768
3769     if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
3770         force_client_lock) {
3771
3772         /* clear the error if we are forcing a client lock, so we
3773            don't get confused later. */
3774         if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
3775             code = 0;
3776
3777         lock_ObtainWrite(&cm_scacheLock);
3778         fileLock = cm_GetFileLock();
3779         lock_ReleaseWrite(&cm_scacheLock);
3780 #ifdef DEBUG
3781         fileLock->fid = scp->fid;
3782 #endif
3783         fileLock->key = key;
3784         fileLock->lockType = Which;
3785         cm_HoldUser(userp);
3786         fileLock->userp = userp;
3787         fileLock->range = range;
3788         fileLock->flags = (code == 0 ? 0 : 
3789                            ((wait_unlock)?
3790                             CM_FILELOCK_FLAG_WAITUNLOCK :
3791                             CM_FILELOCK_FLAG_WAITLOCK));
3792
3793         if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
3794             fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
3795
3796         fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
3797
3798         lock_ObtainWrite(&cm_scacheLock);
3799         osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
3800         cm_HoldSCacheNoLock(scp);
3801         fileLock->scp = scp;
3802         osi_QAdd(&cm_allFileLocks, &fileLock->q);
3803         lock_ReleaseWrite(&cm_scacheLock);
3804
3805         if (code != 0) {
3806             *lockpp = fileLock;
3807         }
3808
3809         if (IS_LOCK_CLIENTONLY(fileLock)) {
3810             scp->clientLocks++;
3811         } else if (IS_LOCK_ACCEPTED(fileLock)) {
3812             if (Which == LockRead)
3813                 scp->sharedLocks++;
3814             else
3815                 scp->exclusiveLocks++;
3816         }
3817
3818         osi_Log3(afsd_logp,
3819                  "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
3820                  fileLock, fileLock->flags, scp);
3821         osi_Log4(afsd_logp,
3822                  "   exclusives[%d] shared[%d] client[%d] serverLock[%d]",
3823                  scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
3824                  (int)(signed char) scp->serverLock);
3825     } else {
3826         osi_Log1(afsd_logp,
3827                  "cm_Lock Rejecting lock (code = 0x%x)", code);
3828     }
3829
3830     return code;
3831 }
3832
3833 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
3834
3835 /* Called with scp->mx held */
3836 long cm_UnlockByKey(cm_scache_t * scp,
3837                     cm_key_t key,
3838                     int flags,
3839                     cm_user_t * userp,
3840                      cm_req_t * reqp)
3841 {
3842     long code = 0;
3843     cm_file_lock_t *fileLock;
3844     osi_queue_t *q, *qn;
3845     int n_unlocks = 0;
3846
3847     osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key 0x%x:%x flags=0x%x",
3848              scp,
3849              (unsigned long)(key >> 32),
3850              (unsigned long)(key & 0xffffffff),
3851              flags);
3852
3853     lock_ObtainWrite(&cm_scacheLock);
3854
3855     for (q = scp->fileLocksH; q; q = qn) {
3856         qn = osi_QNext(q);
3857
3858         fileLock = (cm_file_lock_t *)
3859             ((char *) q - offsetof(cm_file_lock_t, fileq));
3860
3861 #ifdef DEBUG
3862         osi_Log4(afsd_logp, "   Checking lock[0x%x] range[%d,+%d] type[%d]",
3863                  fileLock,
3864                  (unsigned long) fileLock->range.offset,
3865                  (unsigned long) fileLock->range.length,
3866                 fileLock->lockType);
3867         osi_Log3(afsd_logp, "     key[0x%x:%x] flags[0x%x]",
3868                  (unsigned long)(fileLock->key >> 32),
3869                  (unsigned long)(fileLock->key & 0xffffffff),
3870                  fileLock->flags);
3871
3872         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
3873             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
3874             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3875                      fileLock->fid.cell,
3876                      fileLock->fid.volume,
3877                      fileLock->fid.vnode,
3878                      fileLock->fid.unique);
3879             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3880                      fileLock->scp->fid.cell,
3881                      fileLock->scp->fid.volume,
3882                      fileLock->scp->fid.vnode,
3883                      fileLock->scp->fid.unique);
3884             osi_assert(FALSE);
3885         }
3886 #endif
3887
3888         if (!IS_LOCK_DELETED(fileLock) &&
3889             cm_KeyEquals(fileLock->key, key, flags)) {
3890             osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
3891                     fileLock->range.offset,
3892                     fileLock->range.length,
3893                     fileLock->lockType);
3894
3895             if (scp->fileLocksT == q)
3896                 scp->fileLocksT = osi_QPrev(q);
3897             osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
3898
3899             if (IS_LOCK_CLIENTONLY(fileLock)) {
3900                 scp->clientLocks--;
3901             } else if (IS_LOCK_ACCEPTED(fileLock)) {
3902                 if (fileLock->lockType == LockRead)
3903                     scp->sharedLocks--;
3904                 else
3905                     scp->exclusiveLocks--;
3906             }
3907
3908             fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
3909
3910             cm_ReleaseUser(fileLock->userp);
3911             cm_ReleaseSCacheNoLock(scp);
3912
3913             fileLock->userp = NULL;
3914             fileLock->scp = NULL;
3915
3916             n_unlocks++;
3917         }
3918     }
3919
3920     lock_ReleaseWrite(&cm_scacheLock);
3921
3922     if (n_unlocks == 0) {
3923         osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
3924         osi_Log3(afsd_logp, "   Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
3925                  scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3926         
3927         return 0;
3928     }
3929
3930     osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
3931
3932     osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
3933     osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
3934     osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
3935
3936     if (!SERVERLOCKS_ENABLED(scp)) {
3937         osi_Log0(afsd_logp, "  Skipping server lock for scp");
3938         goto done;
3939     }
3940
3941     /* Ideally we would go through the rest of the locks to determine
3942      * if one or more locks that were formerly in WAITUNLOCK can now
3943      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
3944      * scp->sharedLocks accordingly.  However, the retrying of locks
3945      * in that manner is done cm_RetryLock() manually.
3946      */
3947
3948     if (scp->serverLock == LockWrite &&
3949         scp->exclusiveLocks == 0 &&
3950         scp->sharedLocks > 0) {
3951
3952         /* The serverLock should be downgraded to LockRead */
3953         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
3954
3955         /* since scp->serverLock looked sane, we are going to assume
3956            that we have a valid server lock. */
3957         scp->lockDataVersion = scp->dataVersion;
3958         osi_Log1(afsd_logp, "  dataVersion on scp = %d", scp->dataVersion);
3959
3960         code = cm_IntReleaseLock(scp, userp, reqp);
3961
3962         if (code) {
3963             /* so we couldn't release it.  Just let the lock be for now */
3964             code = 0;
3965             goto done;
3966         } else {
3967             scp->serverLock = -1;
3968         }
3969
3970         code = cm_IntSetLock(scp, userp, LockRead, reqp);
3971
3972         if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
3973             scp->serverLock = LockRead;
3974         } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
3975             /* We lost a race condition.  Although we have a valid
3976                lock on the file, the data has changed and essentially
3977                we have lost the lock we had during the transition. */
3978
3979             osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
3980             osi_Log2(afsd_logp, "  Data versions before=%d, after=%d",
3981                      scp->lockDataVersion,
3982                      scp->dataVersion);
3983             
3984             code = cm_IntReleaseLock(scp, userp, reqp);
3985
3986             code = CM_ERROR_INVAL;
3987             scp->serverLock = -1;
3988         }
3989
3990         if (code != 0 &&
3991             (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
3992                 (scp->serverLock == -1)) {
3993                 /* Oopsie */
3994                 cm_LockMarkSCacheLost(scp);
3995             }
3996
3997         /* failure here has no bearing on the return value of
3998            cm_Unlock() */
3999         code = 0;
4000
4001     } else if (scp->serverLock != (-1) &&
4002               scp->exclusiveLocks == 0 &&
4003               scp->sharedLocks == 0) {
4004         /* The serverLock should be released entirely */
4005
4006         code = cm_IntReleaseLock(scp, userp, reqp);
4007
4008         if (code == 0)
4009             scp->serverLock = (-1);
4010     }
4011
4012  done:
4013
4014     osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4015     osi_Log4(afsd_logp, "   Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4016              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4017              (int)(signed char) scp->serverLock);
4018
4019     return code;
4020 }
4021
4022 long cm_Unlock(cm_scache_t *scp, 
4023                unsigned char sLockType,
4024                LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4025                cm_key_t key, 
4026                cm_user_t *userp, 
4027                cm_req_t *reqp)
4028 {
4029     long code = 0;
4030     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4031     cm_file_lock_t *fileLock;
4032     osi_queue_t *q;
4033     int release_userp = FALSE;
4034
4035     osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4036              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4037     osi_Log2(afsd_logp, "... key 0x%x:%x",
4038              (unsigned long) (key >> 32), (unsigned long) (key & 0xffffffff));
4039
4040     lock_ObtainRead(&cm_scacheLock);
4041
4042     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4043         fileLock = (cm_file_lock_t *)
4044             ((char *) q - offsetof(cm_file_lock_t, fileq));
4045
4046 #ifdef DEBUG
4047         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4048             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4049             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4050                      fileLock->fid.cell,
4051                      fileLock->fid.volume,
4052                      fileLock->fid.vnode,
4053                      fileLock->fid.unique);
4054             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4055                      fileLock->scp->fid.cell,
4056                      fileLock->scp->fid.volume,
4057                      fileLock->scp->fid.vnode,
4058                      fileLock->scp->fid.unique);
4059             osi_assert(FALSE);
4060         }
4061 #endif
4062         if (!IS_LOCK_DELETED(fileLock) &&
4063             fileLock->key == key &&
4064             fileLock->range.offset == LOffset.QuadPart &&
4065             fileLock->range.length == LLength.QuadPart) {
4066             break;
4067         }
4068     }
4069
4070     if (!q) {
4071         osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
4072         
4073         lock_ReleaseRead(&cm_scacheLock);
4074
4075         /* The lock didn't exist anyway. *shrug* */
4076         return 0;
4077     }
4078
4079     lock_ReleaseRead(&cm_scacheLock);
4080
4081     /* discard lock record */
4082     lock_ObtainWrite(&cm_scacheLock);
4083     if (scp->fileLocksT == q)
4084         scp->fileLocksT = osi_QPrev(q);
4085     osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4086
4087     /*
4088      * Don't delete it here; let the daemon delete it, to simplify
4089      * the daemon's traversal of the list.
4090      */
4091
4092     if (IS_LOCK_CLIENTONLY(fileLock)) {
4093         scp->clientLocks--;
4094     } else if (IS_LOCK_ACCEPTED(fileLock)) {
4095         if (fileLock->lockType == LockRead)
4096             scp->sharedLocks--;
4097         else
4098             scp->exclusiveLocks--;
4099     }
4100
4101     fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4102     if (userp != NULL) {
4103         cm_ReleaseUser(fileLock->userp);
4104     } else {
4105         userp = fileLock->userp;
4106         release_userp = TRUE;
4107     }
4108     fileLock->userp = NULL;
4109     cm_ReleaseSCacheNoLock(scp);
4110     fileLock->scp = NULL;
4111     lock_ReleaseWrite(&cm_scacheLock);
4112
4113     if (!SERVERLOCKS_ENABLED(scp)) {
4114         osi_Log0(afsd_logp, "   Skipping server locks for scp");
4115         goto done;
4116     }
4117
4118     /* Ideally we would go through the rest of the locks to determine
4119      * if one or more locks that were formerly in WAITUNLOCK can now
4120      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4121      * scp->sharedLocks accordingly.  However, the retrying of locks
4122      * in that manner is done cm_RetryLock() manually.
4123      */
4124
4125     if (scp->serverLock == LockWrite &&
4126         scp->exclusiveLocks == 0 &&
4127         scp->sharedLocks > 0) {
4128
4129         /* The serverLock should be downgraded to LockRead */
4130         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
4131
4132         /* Since we already had a lock, we assume that there is a
4133            valid server lock. */
4134         scp->lockDataVersion = scp->dataVersion;
4135         osi_Log1(afsd_logp, "   dataVersion on scp is %d", scp->dataVersion);
4136
4137         code = cm_IntReleaseLock(scp, userp, reqp);
4138
4139         if (code) {
4140             /* so we couldn't release it.  Just let the lock be for now */
4141             code = 0;
4142             goto done;
4143         } else {
4144             scp->serverLock = -1;
4145         }
4146
4147         code = cm_IntSetLock(scp, userp, LockRead, reqp);
4148
4149         if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4150             scp->serverLock = LockRead;
4151         } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4152             /* Lost a race.  We obtained a new lock, but that is
4153                meaningless since someone modified the file
4154                inbetween. */
4155
4156             osi_Log0(afsd_logp,
4157                      "Data version mismatch while downgrading lock");
4158             osi_Log2(afsd_logp,
4159                      "  Data versions before=%d, after=%d",
4160                      scp->lockDataVersion,
4161                      scp->dataVersion);
4162             
4163             code = cm_IntReleaseLock(scp, userp, reqp);
4164
4165             scp->serverLock = -1;
4166             code = CM_ERROR_INVAL;
4167         }
4168
4169         if (code != 0 &&
4170             (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4171                 (scp->serverLock == -1)) {
4172                 /* Oopsie */
4173                 cm_LockMarkSCacheLost(scp);
4174             }
4175
4176         /* failure here has no bearing on the return value of
4177            cm_Unlock() */
4178         code = 0;
4179
4180     } else if (scp->serverLock != (-1) &&
4181               scp->exclusiveLocks == 0 &&
4182               scp->sharedLocks == 0) {
4183         /* The serverLock should be released entirely */
4184
4185         code = cm_IntReleaseLock(scp, userp, reqp);
4186
4187         if (code == 0) {
4188             scp->serverLock = (-1);
4189         }
4190     }
4191
4192     if (release_userp)
4193         cm_ReleaseUser(userp);
4194
4195  done:
4196
4197     osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
4198     osi_Log4(afsd_logp, "  leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4199              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4200              (int)(signed char) scp->serverLock);
4201
4202     return code;
4203 }
4204
4205 /* called with scp->mx held */
4206 static void cm_LockMarkSCacheLost(cm_scache_t * scp)
4207 {
4208     cm_file_lock_t *fileLock;
4209     osi_queue_t *q;
4210
4211     osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
4212
4213 #ifdef DEBUG
4214     /* With the current code, we can't lose a lock on a RO scp */
4215     osi_assert(!(scp->flags & CM_SCACHEFLAG_RO));
4216 #endif
4217
4218     /* cm_scacheLock needed because we are modifying fileLock->flags */
4219     lock_ObtainWrite(&cm_scacheLock);
4220
4221     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4222         fileLock = 
4223             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4224
4225         if (IS_LOCK_ACTIVE(fileLock) &&
4226             !IS_LOCK_CLIENTONLY(fileLock)) {
4227             if (fileLock->lockType == LockRead)
4228                 scp->sharedLocks--;
4229             else
4230                 scp->exclusiveLocks--;
4231
4232             fileLock->flags |= CM_FILELOCK_FLAG_LOST;
4233         }
4234     }
4235
4236     scp->serverLock = -1;
4237     scp->lockDataVersion = -1;
4238     lock_ReleaseWrite(&cm_scacheLock);
4239 }
4240
4241 /* Called with no relevant locks held */
4242 void cm_CheckLocks()
4243 {
4244     osi_queue_t *q, *nq;
4245     cm_file_lock_t *fileLock;
4246     cm_req_t req;
4247     AFSFid tfid;
4248     AFSVolSync volSync;
4249     cm_conn_t *connp;
4250     long code;
4251     struct rx_connection * callp;
4252     cm_scache_t * scp;
4253
4254     cm_InitReq(&req);
4255
4256     lock_ObtainWrite(&cm_scacheLock);
4257
4258     cm_lockRefreshCycle++;
4259
4260     osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
4261
4262     for (q = cm_allFileLocks; q; q = nq) {
4263         fileLock = (cm_file_lock_t *) q;
4264         nq = osi_QNext(q);
4265         code = -1;
4266
4267         if (IS_LOCK_DELETED(fileLock)) {
4268
4269             osi_QRemove(&cm_allFileLocks, q);
4270             cm_PutFileLock(fileLock);
4271
4272         } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
4273
4274             /* Server locks must have been enabled for us to have
4275                received an active non-client-only lock. */
4276             osi_assert(cm_enableServerLocks);
4277
4278             scp = fileLock->scp;
4279             osi_assert(scp != NULL);
4280
4281             cm_HoldSCacheNoLock(scp);
4282
4283 #ifdef DEBUG
4284             if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4285                 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4286                 osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4287                          fileLock->fid.cell,
4288                          fileLock->fid.volume,
4289                          fileLock->fid.vnode,
4290                          fileLock->fid.unique);
4291                 osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4292                          fileLock->scp->fid.cell,
4293                          fileLock->scp->fid.volume,
4294                          fileLock->scp->fid.vnode,
4295                          fileLock->scp->fid.unique);
4296                 osi_assert(FALSE);
4297             }
4298 #endif
4299             /* Server locks are extended once per scp per refresh
4300                cycle. */
4301             if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
4302
4303                 int scp_done = FALSE;
4304
4305                 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
4306
4307                 lock_ReleaseWrite(&cm_scacheLock);
4308                 lock_ObtainMutex(&scp->mx);
4309
4310                 /* did the lock change while we weren't holding the lock? */
4311                 if (!IS_LOCK_ACTIVE(fileLock))
4312                     goto post_syncopdone;
4313
4314                 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
4315                                  CM_SCACHESYNC_NEEDCALLBACK
4316                                  | CM_SCACHESYNC_GETSTATUS
4317                                  | CM_SCACHESYNC_LOCK);
4318
4319                 if (code) {
4320                     osi_Log1(smb_logp,
4321                              "cm_CheckLocks SyncOp failure code 0x%x", code);
4322                     goto post_syncopdone;
4323                 }
4324
4325                 /* cm_SyncOp releases scp->mx during which the lock
4326                    may get released. */
4327                 if (!IS_LOCK_ACTIVE(fileLock))
4328                     goto pre_syncopdone;
4329
4330                 if (scp->serverLock != -1) {
4331                     cm_fid_t cfid;
4332                     cm_user_t * userp;
4333
4334                     tfid.Volume = scp->fid.volume;
4335                     tfid.Vnode = scp->fid.vnode;
4336                     tfid.Unique = scp->fid.unique;
4337                     cfid = scp->fid;
4338                     userp = fileLock->userp;
4339                     
4340                     osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d", 
4341                              fileLock,
4342                              scp,
4343                              (int) scp->serverLock);
4344
4345                     lock_ReleaseMutex(&scp->mx);
4346
4347                     do {
4348                         code = cm_Conn(&cfid, userp,
4349                                        &req, &connp);
4350                         if (code) 
4351                             break;
4352
4353                         callp = cm_GetRxConn(connp);
4354                         code = RXAFS_ExtendLock(callp, &tfid,
4355                                                 &volSync);
4356                         rx_PutConnection(callp);
4357
4358                         osi_Log1(afsd_logp, "   ExtendLock returns %d", code);
4359
4360                     } while (cm_Analyze(connp, userp, &req,
4361                                         &cfid, &volSync, NULL, NULL,
4362                                         code));
4363
4364                     code = cm_MapRPCError(code, &req);
4365
4366                     lock_ObtainMutex(&scp->mx);
4367
4368                     if (code) {
4369                         osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
4370                     } else {
4371                         osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
4372                         scp->lockDataVersion = scp->dataVersion;
4373                     }
4374
4375                     if ((code == EINVAL || code == CM_ERROR_INVAL) &&
4376                         scp->lockDataVersion == scp->dataVersion) {
4377                         int lockType;
4378
4379                         lockType =
4380                             (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
4381
4382                         /* we might still have a chance to obtain a
4383                            new lock */
4384
4385                         code = cm_IntSetLock(scp, userp, lockType, &req);
4386
4387                         if (code) {
4388                             code = CM_ERROR_INVAL;
4389                         } else if (scp->lockDataVersion != scp->dataVersion) {
4390
4391                             /* now check if we still have the file at
4392                                the right data version. */
4393                             osi_Log1(afsd_logp,
4394                                      "Data version mismatch on scp 0x%p",
4395                                      scp);
4396                             osi_Log2(afsd_logp,
4397                                      "   Data versions: before=%d, after=%d",
4398                                      scp->lockDataVersion,
4399                                      scp->dataVersion);
4400
4401                             code = cm_IntReleaseLock(scp, userp, &req);
4402
4403                             code = CM_ERROR_INVAL;
4404                         }
4405                     }
4406
4407                     if (code == EINVAL || code == CM_ERROR_INVAL) {
4408                         cm_LockMarkSCacheLost(scp);
4409                     }
4410
4411                 } else {
4412                     /* interestingly, we have found an active lock
4413                        belonging to an scache that has no
4414                        serverLock */
4415                     cm_LockMarkSCacheLost(scp);
4416                 }
4417
4418                 scp_done = TRUE;
4419
4420             pre_syncopdone:
4421
4422                 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
4423
4424             post_syncopdone:
4425                 lock_ReleaseMutex(&scp->mx);
4426
4427                 lock_ObtainWrite(&cm_scacheLock);
4428
4429                 if (code == 0) {
4430                     fileLock->lastUpdate = time(NULL);
4431                 }
4432                 
4433                 if (scp_done)
4434                     scp->lastRefreshCycle = cm_lockRefreshCycle;
4435
4436             } else {
4437                 /* we have already refreshed the locks on this scp */
4438                 fileLock->lastUpdate = time(NULL);
4439             }
4440
4441             cm_ReleaseSCacheNoLock(scp);
4442
4443         } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
4444             /* TODO: Check callbacks */
4445         }
4446     }
4447
4448     lock_ReleaseWrite(&cm_scacheLock);
4449     osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
4450 }
4451
4452 /* NOT called with scp->mx held. */
4453 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
4454 {
4455     long code = 0;
4456     cm_scache_t *scp = NULL;
4457     cm_file_lock_t *fileLock;
4458     osi_queue_t *q;
4459     cm_req_t req;
4460     int newLock = -1;
4461     int force_client_lock = FALSE;
4462
4463     cm_InitReq(&req);
4464
4465     if (client_is_dead) {
4466         code = CM_ERROR_TIMEDOUT;
4467         goto updateLock;
4468     }
4469
4470     lock_ObtainRead(&cm_scacheLock);
4471
4472     osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
4473     osi_Log4(afsd_logp, "    offset(%x:%x) length(%x:%x)",
4474              (unsigned)(oldFileLock->range.offset >> 32),
4475              (unsigned)(oldFileLock->range.offset & 0xffffffff),
4476              (unsigned)(oldFileLock->range.length >> 32),
4477              (unsigned)(oldFileLock->range.length & 0xffffffff));
4478     osi_Log3(afsd_logp, "    key(%x:%x) flags=%x",
4479              (unsigned)(oldFileLock->key >> 32),
4480              (unsigned)(oldFileLock->key & 0xffffffff),
4481              (unsigned)(oldFileLock->flags));
4482
4483     /* if the lock has already been granted, then we have nothing to do */
4484     if (IS_LOCK_ACTIVE(oldFileLock)) {
4485         lock_ReleaseRead(&cm_scacheLock);
4486         osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
4487         return 0;
4488     }
4489
4490     /* we can't do anything with lost or deleted locks at the moment. */
4491     if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
4492         code = CM_ERROR_BADFD;
4493         osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
4494         lock_ReleaseRead(&cm_scacheLock);
4495         goto updateLock;
4496     }
4497
4498     scp = oldFileLock->scp;
4499
4500     osi_assert(scp != NULL);
4501
4502     lock_ReleaseRead(&cm_scacheLock);
4503     lock_ObtainMutex(&scp->mx);
4504
4505     code = cm_LockCheckPerms(scp, oldFileLock->lockType,
4506                              oldFileLock->userp,
4507                              &req);
4508
4509     if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
4510         force_client_lock = TRUE;
4511         code = 0;
4512     } else if (code) {
4513         lock_ReleaseMutex(&scp->mx);
4514         return code;
4515     }
4516
4517     lock_ObtainWrite(&cm_scacheLock);
4518
4519     /* Check if we already have a sufficient server lock to allow this
4520        lock to go through. */
4521     if (IS_LOCK_WAITLOCK(oldFileLock) &&
4522         (!SERVERLOCKS_ENABLED(scp) ||
4523          scp->serverLock == oldFileLock->lockType ||
4524          scp->serverLock == LockWrite)) {
4525
4526         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4527
4528         if (SERVERLOCKS_ENABLED(scp)) {
4529             osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock.  Granting",
4530                      (int) scp->serverLock);
4531         } else {
4532             osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
4533         }
4534
4535         lock_ReleaseWrite(&cm_scacheLock);
4536         lock_ReleaseMutex(&scp->mx);
4537
4538         return 0;
4539     }
4540
4541     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
4542
4543         /* check if the conflicting locks have dissappeared already */
4544         for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4545
4546             fileLock = (cm_file_lock_t *)
4547                 ((char *) q - offsetof(cm_file_lock_t, fileq));
4548
4549             if (IS_LOCK_LOST(fileLock)) {
4550                 if (fileLock->key == oldFileLock->key) {
4551                     code = CM_ERROR_BADFD;
4552                     oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
4553                     osi_Log1(afsd_logp, "    found lost lock %p for same key.  Marking lock as lost",
4554                              fileLock);
4555                     break;
4556                 } else if (fileLock->lockType == LockWrite &&
4557                            INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
4558                     osi_Log1(afsd_logp, "    found conflicting LOST lock %p", fileLock);
4559                     code = CM_ERROR_WOULDBLOCK;
4560                     break;
4561                 }
4562             }
4563
4564             if (IS_LOCK_ACCEPTED(fileLock) &&
4565                 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
4566
4567                 if (oldFileLock->lockType != LockRead ||
4568                    fileLock->lockType != LockRead) {
4569
4570                     osi_Log1(afsd_logp, "    found conflicting lock %p", fileLock);
4571                     code = CM_ERROR_WOULDBLOCK;
4572                     break;
4573                 }
4574             }
4575         }
4576     }
4577
4578     if (code != 0) {
4579         lock_ReleaseWrite(&cm_scacheLock);
4580         lock_ReleaseMutex(&scp->mx);
4581
4582         goto handleCode;
4583     }
4584
4585     /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
4586        If it is WAITUNLOCK, then we didn't find any conflicting lock
4587        but we haven't verfied whether the serverLock is sufficient to
4588        assert it.  If it is WAITLOCK, then the serverLock is
4589        insufficient to assert it. Eitherway, we are ready to accept
4590        the lock as either ACTIVE or WAITLOCK depending on the
4591        serverLock. */
4592
4593     /* First, promote the WAITUNLOCK to a WAITLOCK */
4594     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
4595         if (oldFileLock->lockType == LockRead)
4596             scp->sharedLocks++;
4597         else
4598             scp->exclusiveLocks++;
4599
4600         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
4601         oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
4602     }
4603
4604     if (force_client_lock ||
4605         !SERVERLOCKS_ENABLED(scp) ||
4606         scp->serverLock == oldFileLock->lockType ||
4607         (oldFileLock->lockType == LockRead &&
4608          scp->serverLock == LockWrite)) {
4609
4610         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4611
4612         if ((force_client_lock ||
4613              !SERVERLOCKS_ENABLED(scp)) &&
4614             !IS_LOCK_CLIENTONLY(oldFileLock)) {
4615
4616             oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4617
4618             if (oldFileLock->lockType == LockRead)
4619                 scp->sharedLocks--;
4620             else
4621                 scp->exclusiveLocks--;
4622
4623             scp->clientLocks++;
4624         }
4625
4626         lock_ReleaseWrite(&cm_scacheLock);
4627         lock_ReleaseMutex(&scp->mx);
4628
4629         return 0;
4630
4631     } else {
4632         cm_user_t * userp;
4633
4634         code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
4635                          CM_SCACHESYNC_NEEDCALLBACK
4636                          | CM_SCACHESYNC_GETSTATUS
4637                          | CM_SCACHESYNC_LOCK);
4638         if (code) {
4639             osi_Log1(smb_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
4640             lock_ReleaseWrite(&cm_scacheLock);
4641             goto post_syncopdone;
4642         }
4643
4644         if (!IS_LOCK_WAITLOCK(oldFileLock))
4645             goto pre_syncopdone;
4646
4647         userp = oldFileLock->userp;
4648
4649 #ifndef AGGRESSIVE_LOCKS
4650         newLock = oldFileLock->lockType;
4651 #else
4652         newLock = LockWrite;
4653 #endif
4654
4655         lock_ReleaseWrite(&cm_scacheLock);
4656
4657         code = cm_IntSetLock(scp, userp, newLock, &req);
4658
4659     pre_syncopdone:
4660         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
4661     post_syncopdone:
4662         ;
4663     }
4664
4665   handleCode:
4666     if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
4667         lock_ObtainWrite(&cm_scacheLock);
4668         if (scp->fileLocksT == &oldFileLock->fileq)
4669             scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
4670         osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
4671         lock_ReleaseWrite(&cm_scacheLock);
4672     } else if (code == 0 && IS_LOCK_WAITLOCK(oldFileLock)) {
4673         scp->serverLock = newLock;
4674     }
4675     lock_ReleaseMutex(&scp->mx);
4676
4677   updateLock:
4678     lock_ObtainWrite(&cm_scacheLock);
4679     if (code == 0) {
4680         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4681     } else if (code != CM_ERROR_WOULDBLOCK) {
4682         oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4683         cm_ReleaseUser(oldFileLock->userp);
4684         oldFileLock->userp = NULL;
4685         if (oldFileLock->scp) {
4686             cm_ReleaseSCacheNoLock(oldFileLock->scp);
4687             oldFileLock->scp = NULL;
4688         }
4689     }
4690     lock_ReleaseWrite(&cm_scacheLock);
4691
4692     return code;
4693 }
4694
4695 cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
4696 {
4697 #ifdef DEBUG
4698     osi_assert((process_id & 0xffffffff) == process_id);
4699     osi_assert((session_id & 0xffff) == session_id);
4700     osi_assert((file_id & 0xffff) == file_id);
4701 #endif
4702
4703     return 
4704         (((cm_key_t) (process_id & 0xffffffff)) << 32) |
4705         (((cm_key_t) (session_id & 0xffff)) << 16) |
4706         (((cm_key_t) (file_id & 0xffff)));
4707 }
4708
4709 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags)
4710 {
4711     if (flags & CM_UNLOCK_BY_FID) {
4712         return ((k1 & 0xffffffff) == (k2 & 0xffffffff));
4713     } else {
4714         return (k1 == k2);
4715     }
4716 }
4717
4718 void cm_ReleaseAllLocks(void)
4719 {
4720     cm_scache_t *scp;
4721     cm_req_t req;
4722     cm_user_t *userp;
4723     cm_key_t   key;
4724     cm_file_lock_t *fileLock;
4725     unsigned int i;
4726
4727     for (i = 0; i < cm_data.hashTableSize; i++)
4728     {
4729         for ( scp = cm_data.hashTablep[i]; scp; scp = scp->nextp ) {
4730             while (scp->fileLocksH != NULL) {
4731                 lock_ObtainMutex(&scp->mx);
4732                 lock_ObtainWrite(&cm_scacheLock);
4733                 if (!scp->fileLocksH) {
4734                     lock_ReleaseWrite(&cm_scacheLock);
4735                     lock_ReleaseMutex(&scp->mx);
4736                     break;
4737                 }
4738                 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
4739                 userp = fileLock->userp;
4740                 cm_HoldUser(userp);
4741                 key = fileLock->key;
4742                 cm_HoldSCacheNoLock(scp);
4743                 lock_ReleaseWrite(&cm_scacheLock);
4744                 cm_UnlockByKey(scp, key, 0, userp, &req);
4745                 cm_ReleaseSCache(scp);
4746                 cm_ReleaseUser(userp);
4747                 lock_ReleaseMutex(&scp->mx);
4748             }
4749         }
4750     }
4751 }