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