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