windows-byte-range-locking-20060107
[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 = 0;
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     osi_assert(mpNamep[0]);
973     tlen = (int)strlen(scp->mountPointStringp);
974     mtType = *scp->mountPointStringp;
975     cellNamep = malloc(tlen);
976     volNamep = malloc(tlen);
977
978     cp = strrchr(mpNamep, ':');
979     if (cp) {
980         /* cellular mount point */
981         memset(cellNamep, 0, tlen);
982         strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
983         strcpy(volNamep, cp+1);
984         /* now look up the cell */
985         cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
986     }
987     else {
988         /* normal mt pt */
989         strcpy(volNamep, mpNamep+1);
990
991         cellp = cm_FindCellByID(scp->fid.cell);
992     }
993
994     if (!cellp) {
995         code = CM_ERROR_NOSUCHCELL;
996         goto done;
997     }
998
999     vnLength = strlen(volNamep);
1000     if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
1001         type = BACKVOL;
1002     else if (vnLength >= 10
1003               && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
1004         type = ROVOL;
1005     else
1006         type = RWVOL;
1007
1008     /* check for backups within backups */
1009     if (type == BACKVOL
1010          && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
1011          == CM_SCACHEFLAG_RO) {
1012         code = CM_ERROR_NOSUCHVOLUME;
1013         goto done;
1014     }
1015
1016     /* now we need to get the volume */
1017     lock_ReleaseMutex(&scp->mx);
1018     code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
1019     lock_ObtainMutex(&scp->mx);
1020         
1021     if (code == 0) {
1022         /* save the parent of the volume root for this is the 
1023          * place where the volume is mounted and we must remember 
1024          * this in the volume structure rather than just in the 
1025          * scache entry lest the scache entry gets recycled 
1026          * (defect 11489)
1027          */
1028         lock_ObtainMutex(&volp->mx);
1029         volp->dotdotFid = dscp->fid;
1030         lock_ReleaseMutex(&volp->mx);
1031
1032         scp->mountRootFid.cell = cellp->cellID;
1033         /* if the mt pt is in a read-only volume (not just a
1034          * backup), and if there is a read-only volume for the
1035          * target, and if this is a type '#' mount point, use
1036          * the read-only, otherwise use the one specified.
1037          */
1038         if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
1039              && volp->roID != 0 && type == RWVOL)
1040             type = ROVOL;
1041         if (type == ROVOL)
1042             scp->mountRootFid.volume = volp->roID;
1043         else if (type == BACKVOL)
1044             scp->mountRootFid.volume = volp->bkID;
1045         else
1046             scp->mountRootFid.volume = volp->rwID;
1047
1048         /* the rest of the fid is a magic number */
1049         scp->mountRootFid.vnode = 1;
1050         scp->mountRootFid.unique = 1;
1051         scp->mountRootGen = cm_data.mountRootGen;
1052
1053         tfid = scp->mountRootFid;
1054         lock_ReleaseMutex(&scp->mx);
1055         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1056         lock_ObtainMutex(&scp->mx);
1057     }
1058
1059   done:
1060     free(cellNamep);
1061     free(volNamep);
1062     return code;
1063 }       
1064
1065 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1066                        cm_req_t *reqp, cm_scache_t **outpScpp)
1067 {
1068     long code;
1069     int dnlcHit = 1;    /* did we hit in the dnlc? yes, we did */
1070     cm_scache_t *tscp = NULL;
1071     cm_scache_t *mountedScp;
1072     cm_lookupSearch_t rock;
1073     int getroot;
1074
1075     if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1076          && strcmp(namep, "..") == 0) {
1077         if (dscp->dotdotFid.volume == 0)
1078             return CM_ERROR_NOSUCHVOLUME;
1079         rock.fid = dscp->dotdotFid;
1080         goto haveFid;
1081     }
1082
1083     memset(&rock, 0, sizeof(rock));
1084     rock.fid.cell = dscp->fid.cell;
1085     rock.fid.volume = dscp->fid.volume;
1086     rock.searchNamep = namep;
1087     rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1088     rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
1089
1090     /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1091     code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1092                         (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1093
1094     /* code == 0 means we fell off the end of the dir, while stopnow means
1095      * that we stopped early, probably because we found the entry we're
1096      * looking for.  Any other non-zero code is an error.
1097      */
1098     if (code && code != CM_ERROR_STOPNOW) {
1099         /* if the cm_scache_t we are searching in is not a directory 
1100          * we must return path not found because the error 
1101          * is to describe the final component not an intermediary
1102          */
1103         if (code == CM_ERROR_NOTDIR) {
1104             if (flags & CM_FLAG_CHECKPATH)
1105                 return CM_ERROR_NOSUCHPATH;
1106             else
1107                 return CM_ERROR_NOSUCHFILE;
1108         }
1109         return code;
1110     }
1111
1112     getroot = (dscp==cm_data.rootSCachep) ;
1113     if (!rock.found) {
1114         if (!cm_freelanceEnabled || !getroot) {
1115             if (flags & CM_FLAG_CHECKPATH)
1116                 return CM_ERROR_NOSUCHPATH;
1117             else
1118                 return CM_ERROR_NOSUCHFILE;
1119         }
1120         else {  /* nonexistent dir on freelance root, so add it */
1121             char fullname[200] = ".";
1122             int  found = 0;
1123
1124             osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s", 
1125                       osi_LogSaveString(afsd_logp,namep));
1126             if (namep[0] == '.') {
1127                 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1128                     found = 1;
1129                     if ( stricmp(&namep[1], &fullname[1]) )
1130                         code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1131                     else
1132                         code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1133                 }
1134             } else {
1135                 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1136                     found = 1;
1137                     if ( stricmp(namep, fullname) )
1138                         code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1139                     else
1140                         code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1141                 }
1142             }
1143             if (!found || code < 0) {   /* add mount point failed, so give up */
1144                 if (flags & CM_FLAG_CHECKPATH)
1145                     return CM_ERROR_NOSUCHPATH;
1146                 else
1147                     return CM_ERROR_NOSUCHFILE;
1148             }
1149             tscp = NULL;   /* to force call of cm_GetSCache */
1150         }
1151     }
1152
1153   haveFid:       
1154     if ( !tscp )    /* we did not find it in the dnlc */
1155     {
1156         dnlcHit = 0;    
1157         code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1158         if (code) 
1159             return code;
1160     }       
1161     /* tscp is now held */
1162
1163     lock_ObtainMutex(&tscp->mx);
1164     code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1165                       CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1166     if (code) { 
1167         lock_ReleaseMutex(&tscp->mx);
1168         cm_ReleaseSCache(tscp);
1169         return code;
1170     }
1171     /* tscp is now locked */
1172
1173     if (!(flags & CM_FLAG_NOMOUNTCHASE)
1174          && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1175         /* mount points are funny: they have a volume name to mount
1176          * the root of.
1177          */
1178         code = cm_ReadMountPoint(tscp, userp, reqp);
1179         if (code == 0)
1180             code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1181                                         &mountedScp);
1182         lock_ReleaseMutex(&tscp->mx);
1183         cm_ReleaseSCache(tscp);
1184         if (code) {
1185             return code;
1186         }
1187         tscp = mountedScp;
1188     }
1189     else {
1190         lock_ReleaseMutex(&tscp->mx);
1191     }
1192
1193     /* copy back pointer */
1194     *outpScpp = tscp;
1195
1196     /* insert scache in dnlc */
1197     if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1198         /* lock the directory entry to prevent racing callback revokes */
1199         lock_ObtainMutex(&dscp->mx);
1200         if ( dscp->cbServerp && dscp->cbExpires )
1201             cm_dnlcEnter(dscp, namep, tscp);
1202         lock_ReleaseMutex(&dscp->mx);
1203     }
1204
1205     /* and return */
1206     return 0;
1207 }
1208
1209 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1210 {
1211     char *tp;
1212     int prefixCount;
1213
1214     tp = strrchr(inp, '@');
1215     if (tp == NULL) 
1216         return 0;               /* no @sys */
1217
1218     if (strcmp(tp, "@sys") != 0) 
1219         return 0;       /* no @sys */
1220
1221     /* caller just wants to know if this is a valid @sys type of name */
1222     if (outp == NULL) 
1223         return 1;
1224
1225     if (index >= MAXNUMSYSNAMES)
1226         return -1;
1227
1228     /* otherwise generate the properly expanded @sys name */
1229     prefixCount = (int)(tp - inp);
1230
1231     strncpy(outp, inp, prefixCount);    /* copy out "a." from "a.@sys" */
1232     outp[prefixCount] = 0;              /* null terminate the "a." */
1233     strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1234     return 1;
1235 }   
1236
1237 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1238                cm_req_t *reqp, cm_scache_t **outpScpp)
1239 {
1240     long code;
1241     char tname[256];
1242     int sysNameIndex = 0;
1243     cm_scache_t *scp = 0;
1244
1245     if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1246         if (flags & CM_FLAG_CHECKPATH)
1247             return CM_ERROR_NOSUCHPATH;
1248         else
1249             return CM_ERROR_NOSUCHFILE;
1250     }
1251
1252     for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1253         code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1254         if (code > 0) {
1255             code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1256             if (code == 0) {
1257                 *outpScpp = scp;
1258                 return 0;
1259             }
1260             if (scp) {
1261                 cm_ReleaseSCache(scp);
1262                 scp = 0;
1263             }
1264         } else {
1265             return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
1266         }
1267     }
1268
1269     /* None of the possible sysName expansions could be found */
1270     if (flags & CM_FLAG_CHECKPATH)
1271         return CM_ERROR_NOSUCHPATH;
1272     else
1273         return CM_ERROR_NOSUCHFILE;
1274 }
1275
1276 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1277 {
1278     long code;
1279     cm_conn_t *connp;
1280     AFSFid afsFid;
1281     int sflags;
1282     AFSFetchStatus newDirStatus;
1283     AFSVolSync volSync;
1284     struct rx_connection * callp;
1285
1286 #ifdef AFS_FREELANCE_CLIENT
1287     if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1288         /* deleting a mount point from the root dir. */
1289         code = cm_FreelanceRemoveMount(namep);
1290         return code;
1291     }
1292 #endif  
1293
1294     /* make sure we don't screw up the dir status during the merge */
1295     lock_ObtainMutex(&dscp->mx);
1296     sflags = CM_SCACHESYNC_STOREDATA;
1297     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1298     lock_ReleaseMutex(&dscp->mx);
1299     if (code) 
1300         return code;
1301
1302     /* make the RPC */
1303     afsFid.Volume = dscp->fid.volume;
1304     afsFid.Vnode = dscp->fid.vnode;
1305     afsFid.Unique = dscp->fid.unique;
1306
1307     osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1308     do {
1309         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1310         if (code) 
1311             continue;
1312
1313         callp = cm_GetRxConn(connp);
1314         code = RXAFS_RemoveFile(callp, &afsFid, namep,
1315                                  &newDirStatus, &volSync);
1316         rx_PutConnection(callp);
1317
1318     } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1319     code = cm_MapRPCError(code, reqp);
1320
1321     if (code)
1322         osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1323     else
1324         osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1325
1326     lock_ObtainMutex(&dscp->mx);
1327     cm_dnlcRemove(dscp, namep);
1328     cm_SyncOpDone(dscp, NULL, sflags);
1329     if (code == 0) 
1330         cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1331     lock_ReleaseMutex(&dscp->mx);
1332
1333     return code;
1334 }
1335
1336 /* called with a locked vnode, and fills in the link info.
1337  * returns this the vnode still locked.
1338  */
1339 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1340 {
1341     long code;
1342     cm_buf_t *bufp;
1343     long temp;
1344     osi_hyper_t thyper;
1345
1346     lock_AssertMutex(&linkScp->mx);
1347     if (!linkScp->mountPointStringp[0]) {
1348         /* read the link data */
1349         lock_ReleaseMutex(&linkScp->mx);
1350         thyper.LowPart = thyper.HighPart = 0;
1351         code = buf_Get(linkScp, &thyper, &bufp);
1352         lock_ObtainMutex(&linkScp->mx);
1353         if (code) 
1354             return code;
1355         while (1) {
1356             code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1357                               CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1358             if (code) {
1359                 buf_Release(bufp);
1360                 return code;
1361             }
1362             if (cm_HaveBuffer(linkScp, bufp, 0)) 
1363                 break;
1364
1365             code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1366             if (code) {
1367                 buf_Release(bufp);
1368                 return code;
1369             }
1370         } /* while loop to get the data */
1371                 
1372         /* now if we still have no link read in,
1373          * copy the data from the buffer */
1374         if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1375             buf_Release(bufp);
1376             return CM_ERROR_TOOBIG;
1377         }
1378
1379         /* otherwise, it fits; make sure it is still null (could have
1380          * lost race with someone else referencing this link above),
1381          * and if so, copy in the data.
1382          */
1383         if (!linkScp->mountPointStringp[0]) {
1384             strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1385             linkScp->mountPointStringp[temp] = 0;       /* null terminate */
1386         }
1387         buf_Release(bufp);
1388     }   /* don't have sym link contents cached */
1389
1390     return 0;
1391 }       
1392
1393 /* called with a held vnode and a path suffix, with the held vnode being a
1394  * symbolic link.  Our goal is to generate a new path to interpret, and return
1395  * this new path in newSpaceBufferp.  If the new vnode is relative to a dir
1396  * other than the directory containing the symbolic link, then the new root is
1397  * returned in *newRootScpp, otherwise a null is returned there.
1398  */
1399 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1400                       cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1401                       cm_user_t *userp, cm_req_t *reqp)
1402 {
1403     long code = 0;
1404     long len;
1405     char *linkp;
1406     cm_space_t *tsp;
1407
1408     lock_ObtainMutex(&linkScp->mx);
1409     code = cm_HandleLink(linkScp, userp, reqp);
1410     if (code) 
1411         goto done;
1412
1413     /* if we may overflow the buffer, bail out; buffer is signficantly
1414      * bigger than max path length, so we don't really have to worry about
1415      * being a little conservative here.
1416      */
1417     if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1418          >= CM_UTILS_SPACESIZE)
1419         return CM_ERROR_TOOBIG;
1420
1421     tsp = cm_GetSpace();
1422     linkp = linkScp->mountPointStringp;
1423     if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1424         if (strlen(linkp) > cm_mountRootLen)
1425             strcpy(tsp->data, linkp+cm_mountRootLen+1);
1426         else
1427             tsp->data[0] = 0;
1428         *newRootScpp = cm_data.rootSCachep;
1429         cm_HoldSCache(cm_data.rootSCachep);
1430     } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1431         if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName)))) 
1432         {
1433             char * p = &linkp[len + 3];
1434             if (strnicmp(p, "all", 3) == 0)
1435                 p += 4;
1436
1437             strcpy(tsp->data, p);
1438             for (p = tsp->data; *p; p++) {
1439                 if (*p == '\\')
1440                     *p = '/';
1441             }
1442             *newRootScpp = cm_data.rootSCachep;
1443             cm_HoldSCache(cm_data.rootSCachep);
1444         } else {
1445             linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1446             strcpy(tsp->data, linkp);
1447             *newRootScpp = NULL;
1448             code = CM_ERROR_PATH_NOT_COVERED;
1449         }
1450     } else if ( !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1451         linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1452         strcpy(tsp->data, linkp);
1453         *newRootScpp = NULL;
1454         code = CM_ERROR_PATH_NOT_COVERED;
1455     } else if (*linkp == '\\' || *linkp == '/') {
1456 #if 0   
1457         /* formerly, this was considered to be from the AFS root,
1458          * but this seems to create problems.  instead, we will just
1459          * reject the link */
1460         strcpy(tsp->data, linkp+1);
1461         *newRootScpp = cm_data.rootSCachep;
1462         cm_HoldSCache(cm_data.rootSCachep);
1463 #else
1464         /* we still copy the link data into the response so that 
1465          * the user can see what the link points to
1466          */
1467         linkScp->fileType = CM_SCACHETYPE_INVALID;
1468         strcpy(tsp->data, linkp);
1469         *newRootScpp = NULL;
1470         code = CM_ERROR_NOSUCHPATH;
1471 #endif  
1472     } else {
1473         /* a relative link */
1474         strcpy(tsp->data, linkp);
1475         *newRootScpp = NULL;
1476     }
1477     if (pathSuffixp[0] != 0) {  /* if suffix string is non-null */
1478         strcat(tsp->data, "\\");
1479         strcat(tsp->data, pathSuffixp);
1480     }
1481     *newSpaceBufferp = tsp;
1482
1483   done:
1484     lock_ReleaseMutex(&linkScp->mx);
1485     return code;
1486 }
1487
1488 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1489                cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1490 {
1491     long code;
1492     char *tp;                   /* ptr moving through input buffer */
1493     char tc;                    /* temp char */
1494     int haveComponent;          /* has new component started? */
1495     char component[256];        /* this is the new component */
1496     char *cp;                   /* component name being assembled */
1497     cm_scache_t *tscp;          /* current location in the hierarchy */
1498     cm_scache_t *nscp;          /* next dude down */
1499     cm_scache_t *dirScp;        /* last dir we searched */
1500     cm_scache_t *linkScp;       /* new root for the symlink we just
1501     * looked up */
1502     cm_space_t *psp;            /* space for current path, if we've hit
1503     * any symlinks */
1504     cm_space_t *tempsp;         /* temp vbl */
1505     char *restp;                /* rest of the pathname to interpret */
1506     int symlinkCount;           /* count of # of symlinks traversed */
1507     int extraFlag;              /* avoid chasing mt pts for dir cmd */
1508     int phase = 1;              /* 1 = tidPathp, 2 = pathp */
1509
1510     tp = tidPathp;
1511     if (tp == NULL) {
1512         tp = pathp;
1513         phase = 2;
1514     }
1515     if (tp == NULL) {
1516         tp = "";
1517     }
1518     haveComponent = 0;
1519     psp = NULL;
1520     tscp = rootSCachep;
1521     cm_HoldSCache(tscp);
1522     symlinkCount = 0;
1523     dirScp = 0;
1524
1525     while (1) {
1526         tc = *tp++;
1527
1528         /* map Unix slashes into DOS ones so we can interpret Unix
1529          * symlinks properly
1530          */
1531         if (tc == '/') 
1532             tc = '\\';
1533
1534         if (!haveComponent) {
1535             if (tc == '\\') {
1536                 continue;
1537             } else if (tc == 0) {
1538                 if (phase == 1) {
1539                     phase = 2;
1540                     tp = pathp;
1541                     continue;
1542                 }
1543                 code = 0;
1544                 break;
1545             } else {
1546                 haveComponent = 1;
1547                 cp = component;
1548                 *cp++ = tc;
1549             }
1550         } else {
1551             /* we have a component here */
1552             if (tc == 0 || tc == '\\') {
1553                 /* end of the component; we're at the last
1554                  * component if tc == 0.  However, if the last
1555                  * is a symlink, we have more to do.
1556                  */
1557                 *cp++ = 0;      /* add null termination */
1558                 extraFlag = 0;
1559                 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1560                     extraFlag = CM_FLAG_NOMOUNTCHASE;
1561                 code = cm_Lookup(tscp, component,
1562                                   flags | extraFlag,
1563                                   userp, reqp, &nscp);
1564                 if (code) {
1565                     cm_ReleaseSCache(tscp);
1566                     if (dirScp)
1567                         cm_ReleaseSCache(dirScp);
1568                     if (psp) 
1569                         cm_FreeSpace(psp);
1570                     if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK)
1571                         return CM_ERROR_NOSUCHPATH;
1572                     else
1573                         return code;
1574                 }
1575                 haveComponent = 0;      /* component done */
1576                 if (dirScp)
1577                     cm_ReleaseSCache(dirScp);
1578                 dirScp = tscp;          /* for some symlinks */
1579                 tscp = nscp;            /* already held */
1580                 nscp = 0;
1581                 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1582                     code = 0;
1583                     if (dirScp) {
1584                         cm_ReleaseSCache(dirScp);
1585                         dirScp = 0;
1586                     }
1587                     break;
1588                 }
1589
1590                 /* now, if tscp is a symlink, we should follow
1591                  * it and assemble the path again.
1592                  */
1593                 lock_ObtainMutex(&tscp->mx);
1594                 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1595                                   CM_SCACHESYNC_GETSTATUS
1596                                   | CM_SCACHESYNC_NEEDCALLBACK);
1597                 if (code) {
1598                     lock_ReleaseMutex(&tscp->mx);
1599                     cm_ReleaseSCache(tscp);
1600                     tscp = 0;
1601                     if (dirScp) {
1602                         cm_ReleaseSCache(dirScp);
1603                         dirScp = 0;
1604                     }
1605                     break;
1606                 }
1607                 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1608                     /* this is a symlink; assemble a new buffer */
1609                     lock_ReleaseMutex(&tscp->mx);
1610                     if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1611                         cm_ReleaseSCache(tscp);
1612                         tscp = 0;
1613                         if (dirScp) {
1614                             cm_ReleaseSCache(dirScp);
1615                             dirScp = 0;
1616                         }
1617                         if (psp) 
1618                             cm_FreeSpace(psp);
1619                         return CM_ERROR_TOO_MANY_SYMLINKS;
1620                     }
1621                     if (tc == 0) 
1622                         restp = "";
1623                     else 
1624                         restp = tp;
1625                     code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1626                     if (code) {
1627                         /* something went wrong */
1628                         cm_ReleaseSCache(tscp);
1629                         tscp = 0;
1630                         if (dirScp) {
1631                             cm_ReleaseSCache(dirScp);
1632                             dirScp = 0;
1633                         }
1634                         break;
1635                     }
1636
1637                     /* otherwise, tempsp has the new path,
1638                      * and linkScp is the new root from
1639                      * which to interpret that path.
1640                      * Continue with the namei processing,
1641                      * also doing the bookkeeping for the
1642                      * space allocation and tracking the
1643                      * vnode reference counts.
1644                      */
1645                     if (psp) 
1646                         cm_FreeSpace(psp);
1647                     psp = tempsp;
1648                     tp = psp->data;
1649                     cm_ReleaseSCache(tscp);
1650                     tscp = linkScp;
1651                     linkScp = 0;
1652                     /* already held
1653                      * by AssembleLink
1654                      * now, if linkScp is null, that's
1655                      * AssembleLink's way of telling us that
1656                      * the sym link is relative to the dir
1657                      * containing the link.  We have a ref
1658                      * to it in dirScp, and we hold it now
1659                      * and reuse it as the new spot in the
1660                      * dir hierarchy.
1661                      */
1662                     if (tscp == NULL) {
1663                         tscp = dirScp;
1664                         dirScp = 0;
1665                     }
1666                 } else {
1667                     /* not a symlink, we may be done */
1668                     lock_ReleaseMutex(&tscp->mx);
1669                     if (tc == 0) {
1670                         if (phase == 1) {
1671                             phase = 2;
1672                             tp = pathp;
1673                             continue;
1674                         }
1675                         if (dirScp) {
1676                             cm_ReleaseSCache(dirScp);
1677                             dirScp = 0;
1678                         }
1679                         code = 0;
1680                         break;
1681                     }
1682                 }
1683                 if (dirScp) {
1684                     cm_ReleaseSCache(dirScp);
1685                     dirScp = 0;
1686                 }
1687             } /* end of a component */
1688             else 
1689                 *cp++ = tc;
1690         } /* we have a component */
1691     } /* big while loop over all components */
1692
1693     /* already held */
1694     if (dirScp)
1695         cm_ReleaseSCache(dirScp);
1696     if (psp) 
1697         cm_FreeSpace(psp);
1698     if (code == 0) 
1699         *outScpp = tscp;
1700     else if (tscp)
1701         cm_ReleaseSCache(tscp);
1702     return code;
1703 }
1704
1705 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1706  * We chase the link, and return a held pointer to the target, if it exists,
1707  * in *outScpp.  If we succeed, we return 0, otherwise we return an error code
1708  * and do not hold or return a target vnode.
1709  *
1710  * This is very similar to calling cm_NameI with the last component of a name,
1711  * which happens to be a symlink, except that we've already passed by the name.
1712  *
1713  * This function is typically called by the directory listing functions, which
1714  * encounter symlinks but need to return the proper file length so programs
1715  * like "more" work properly when they make use of the attributes retrieved from
1716  * the dir listing.
1717  *
1718  * The input vnode should not be locked when this function is called.
1719  */
1720 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1721                          cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1722 {
1723     long code;
1724     cm_space_t *spacep;
1725     cm_scache_t *newRootScp;
1726
1727     osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
1728
1729     code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1730     if (code) 
1731         return code;
1732
1733     /* now, if newRootScp is NULL, we're really being told that the symlink
1734      * is relative to the current directory (dscp).
1735      */
1736     if (newRootScp == NULL) {
1737         newRootScp = dscp;
1738         cm_HoldSCache(dscp);
1739     }
1740
1741     code = cm_NameI(newRootScp, spacep->data,
1742                      CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1743                      userp, NULL, reqp, outScpp);
1744
1745     if (code == CM_ERROR_NOSUCHFILE)
1746         code = CM_ERROR_NOSUCHPATH;
1747
1748     /* this stuff is allocated no matter what happened on the namei call,
1749      * so free it */
1750     cm_FreeSpace(spacep);
1751     cm_ReleaseSCache(newRootScp);
1752
1753     return code;
1754 }
1755
1756 /* make this big enough so that one buffer of dir pages won't overflow.  We'll
1757  * check anyway, but we want to minimize the chance that we have to leave stuff
1758  * unstat'd.
1759  */
1760 #define CM_BULKMAX              128
1761
1762 /* rock for bulk stat calls */
1763 typedef struct cm_bulkStat {
1764     osi_hyper_t bufOffset;      /* only do it for things in this buffer page */
1765
1766     /* info for the actual call */
1767     int counter;                        /* next free slot */
1768     AFSFid fids[CM_BULKMAX];
1769     AFSFetchStatus stats[CM_BULKMAX];
1770     AFSCallBack callbacks[CM_BULKMAX];
1771 } cm_bulkStat_t;
1772
1773 /* for a given entry, make sure that it isn't in the stat cache, and then
1774  * add it to the list of file IDs to be obtained.
1775  *
1776  * Don't bother adding it if we already have a vnode.  Note that the dir
1777  * is locked, so we have to be careful checking the vnode we're thinking of
1778  * processing, to avoid deadlocks.
1779  */
1780 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1781                      osi_hyper_t *offp)
1782 {
1783     osi_hyper_t thyper;
1784     cm_bulkStat_t *bsp;
1785     int i;
1786     cm_scache_t *tscp;
1787     cm_fid_t tfid;
1788
1789     bsp = rockp;
1790
1791     /* Don't overflow bsp. */
1792     if (bsp->counter >= CM_BULKMAX)
1793         return CM_ERROR_STOPNOW;
1794
1795     thyper.LowPart = cm_data.buf_blockSize;
1796     thyper.HighPart = 0;
1797     thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1798
1799     /* thyper is now the first byte past the end of the record we're
1800      * interested in, and bsp->bufOffset is the first byte of the record
1801      * we're interested in.
1802      * Skip data in the others.
1803      * Skip '.' and '..'
1804      */
1805     if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1806         return 0;
1807     if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1808         return CM_ERROR_STOPNOW;
1809     if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1810         return 0;
1811
1812     tfid.cell = scp->fid.cell;
1813     tfid.volume = scp->fid.volume;
1814     tfid.vnode = ntohl(dep->fid.vnode);
1815     tfid.unique = ntohl(dep->fid.unique);
1816     tscp = cm_FindSCache(&tfid);
1817     if (tscp) {
1818         if (lock_TryMutex(&tscp->mx)) {
1819             /* we have an entry that we can look at */
1820             if (cm_HaveCallback(tscp)) {
1821                 /* we have a callback on it.  Don't bother
1822                  * fetching this stat entry, since we're happy
1823                  * with the info we have.
1824                  */
1825                 lock_ReleaseMutex(&tscp->mx);
1826                 cm_ReleaseSCache(tscp);
1827                 return 0;
1828             }
1829             lock_ReleaseMutex(&tscp->mx);
1830         }       /* got lock */
1831         cm_ReleaseSCache(tscp);
1832     }   /* found entry */
1833
1834 #ifdef AFS_FREELANCE_CLIENT
1835     // yj: if this is a mountpoint under root.afs then we don't want it
1836     // to be bulkstat-ed, instead, we call getSCache directly and under
1837     // getSCache, it is handled specially.
1838     if  ( cm_freelanceEnabled &&
1839           tfid.cell==AFS_FAKE_ROOT_CELL_ID && 
1840           tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1841           !(tfid.vnode==0x1 && tfid.unique==0x1) )
1842     {       
1843         osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1844         return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1845     }
1846 #endif /* AFS_FREELANCE_CLIENT */
1847
1848     i = bsp->counter++;
1849     bsp->fids[i].Volume = scp->fid.volume;
1850     bsp->fids[i].Vnode = tfid.vnode;
1851     bsp->fids[i].Unique = tfid.unique;
1852     return 0;
1853 }       
1854
1855 /* called with a locked scp and a pointer to a buffer.  Make bulk stat
1856  * calls on all undeleted files in the page of the directory specified.
1857  */
1858 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1859                      cm_req_t *reqp)
1860 {
1861     long code;
1862     cm_bulkStat_t bb;   /* this is *BIG*, probably 12K or so;
1863                          * watch for stack problems */
1864     AFSCBFids fidStruct;
1865     AFSBulkStats statStruct;
1866     cm_conn_t *connp;
1867     AFSCBs callbackStruct;
1868     long filex;
1869     AFSVolSync volSync;
1870     cm_callbackRequest_t cbReq;
1871     long filesThisCall;
1872     long i;
1873     long j;
1874     cm_scache_t *scp;
1875     cm_fid_t tfid;
1876     struct rx_connection * callp;
1877
1878     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
1879
1880     /* should be on a buffer boundary */
1881     osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
1882
1883     bb.counter = 0;
1884     bb.bufOffset = *offsetp;
1885
1886     lock_ReleaseMutex(&dscp->mx);
1887     /* first, assemble the file IDs we need to stat */
1888     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
1889
1890     /* if we failed, bail out early */
1891     if (code && code != CM_ERROR_STOPNOW) {
1892         lock_ObtainMutex(&dscp->mx);
1893         return;
1894     }
1895
1896     /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1897      * make the calls to create the entries.  Handle AFSCBMAX files at a
1898      * time.
1899      */
1900     filex = 0;
1901     while (filex < bb.counter) {
1902         filesThisCall = bb.counter - filex;
1903         if (filesThisCall > AFSCBMAX) 
1904             filesThisCall = AFSCBMAX;
1905
1906         fidStruct.AFSCBFids_len = filesThisCall;
1907         fidStruct.AFSCBFids_val = &bb.fids[filex];
1908         statStruct.AFSBulkStats_len = filesThisCall;
1909         statStruct.AFSBulkStats_val = &bb.stats[filex];
1910         callbackStruct.AFSCBs_len = filesThisCall;
1911         callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1912         cm_StartCallbackGrantingCall(NULL, &cbReq);
1913         osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1914         do {
1915             code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1916             if (code) 
1917                 continue;
1918
1919             callp = cm_GetRxConn(connp);
1920             code = RXAFS_BulkStatus(callp, &fidStruct,
1921                                      &statStruct, &callbackStruct, &volSync);
1922             rx_PutConnection(callp);
1923
1924         } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1925                              &volSync, NULL, &cbReq, code));
1926         code = cm_MapRPCError(code, reqp);
1927
1928         if (code)
1929             osi_Log1(afsd_logp, "CALL BulkStatus FAILURE code 0x%x", code);
1930         else
1931             osi_Log0(afsd_logp, "CALL BulkStatus SUCCESS");
1932
1933         /* may as well quit on an error, since we're not going to do
1934          * much better on the next immediate call, either.
1935          */
1936         if (code) {
1937             cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1938             break;
1939         }
1940
1941         /* otherwise, we should do the merges */
1942         for (i = 0; i<filesThisCall; i++) {
1943             j = filex + i;
1944             tfid.cell = dscp->fid.cell;
1945             tfid.volume = bb.fids[j].Volume;
1946             tfid.vnode = bb.fids[j].Vnode;
1947             tfid.unique = bb.fids[j].Unique;
1948             code = cm_GetSCache(&tfid, &scp, userp, reqp);
1949             if (code != 0) 
1950                 continue;
1951
1952             /* otherwise, if this entry has no callback info, 
1953              * merge in this.
1954              */
1955             lock_ObtainMutex(&scp->mx);
1956             /* now, we have to be extra paranoid on merging in this
1957              * information, since we didn't use cm_SyncOp before
1958              * starting the fetch to make sure that no bad races
1959              * were occurring.  Specifically, we need to make sure
1960              * we don't obliterate any newer information in the
1961              * vnode than have here.
1962              *
1963              * Right now, be pretty conservative: if there's a
1964              * callback or a pending call, skip it.
1965              */
1966             if (scp->cbServerp == NULL
1967                  && !(scp->flags &
1968                        (CM_SCACHEFLAG_FETCHING
1969                          | CM_SCACHEFLAG_STORING
1970                          | CM_SCACHEFLAG_SIZESTORING))) {
1971                 cm_EndCallbackGrantingCall(scp, &cbReq,
1972                                             &bb.callbacks[j],
1973                                             CM_CALLBACK_MAINTAINCOUNT);
1974                 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1975                                 userp, 0);
1976             }       
1977             lock_ReleaseMutex(&scp->mx);
1978             cm_ReleaseSCache(scp);
1979         } /* all files in the response */
1980         /* now tell it to drop the count,
1981          * after doing the vnode processing above */
1982         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1983
1984         filex += filesThisCall;
1985     }   /* while there are still more files to process */
1986     lock_ObtainMutex(&dscp->mx);
1987     osi_Log0(afsd_logp, "END cm_TryBulkStat");
1988 }       
1989
1990 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1991 {
1992     long mask;
1993
1994     /* initialize store back mask as inexpensive local variable */
1995     mask = 0;
1996     memset(statusp, 0, sizeof(AFSStoreStatus));
1997
1998     /* copy out queued info from scache first, if scp passed in */
1999     if (scp) {
2000         if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2001             statusp->ClientModTime = scp->clientModTime;
2002             mask |= AFS_SETMODTIME;
2003             scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2004         }
2005     }
2006
2007     if (attrp) {
2008         /* now add in our locally generated request */
2009         if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2010             statusp->ClientModTime = attrp->clientModTime;
2011             mask |= AFS_SETMODTIME;
2012         }
2013         if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2014             statusp->UnixModeBits = attrp->unixModeBits;
2015             mask |= AFS_SETMODE;
2016         }
2017         if (attrp->mask & CM_ATTRMASK_OWNER) {
2018             statusp->Owner = attrp->owner;
2019             mask |= AFS_SETOWNER;
2020         }
2021         if (attrp->mask & CM_ATTRMASK_GROUP) {
2022             statusp->Group = attrp->group;
2023             mask |= AFS_SETGROUP;
2024         }
2025     }
2026     statusp->Mask = mask;
2027 }       
2028
2029 /* set the file size, and make sure that all relevant buffers have been
2030  * truncated.  Ensure that any partially truncated buffers have been zeroed
2031  * to the end of the buffer.
2032  */
2033 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2034                    cm_req_t *reqp)
2035 {
2036     long code;
2037     int shrinking;
2038
2039     /* start by locking out buffer creation */
2040     lock_ObtainWrite(&scp->bufCreateLock);
2041
2042     /* verify that this is a file, not a dir or a symlink */
2043     lock_ObtainMutex(&scp->mx);
2044     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2045                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2046     if (code) 
2047         goto done;
2048         
2049     if (scp->fileType != CM_SCACHETYPE_FILE) {
2050         code = CM_ERROR_ISDIR;
2051         goto done;
2052     }
2053
2054   startover:
2055     if (LargeIntegerLessThan(*sizep, scp->length))
2056         shrinking = 1;
2057     else
2058         shrinking = 0;
2059
2060     lock_ReleaseMutex(&scp->mx);
2061
2062     /* can't hold scp->mx lock here, since we may wait for a storeback to
2063      * finish if the buffer package is cleaning a buffer by storing it to
2064      * the server.
2065      */
2066     if (shrinking)
2067         buf_Truncate(scp, userp, reqp, sizep);
2068
2069     /* now ensure that file length is short enough, and update truncPos */
2070     lock_ObtainMutex(&scp->mx);
2071
2072     /* make sure we have a callback (so we have the right value for the
2073      * length), and wait for it to be safe to do a truncate.
2074      */
2075     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2076                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2077                       | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2078     if (code) 
2079         goto done;
2080
2081     if (LargeIntegerLessThan(*sizep, scp->length)) {
2082         /* a real truncation.  If truncPos is not set yet, or is bigger
2083          * than where we're truncating the file, set truncPos to this
2084          * new value.
2085          */
2086         if (!shrinking)
2087             goto startover;
2088         if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2089              || LargeIntegerLessThan(*sizep, scp->length)) {
2090             /* set trunc pos */
2091             scp->truncPos = *sizep;
2092             scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2093         }
2094         /* in either case, the new file size has been changed */
2095         scp->length = *sizep;
2096         scp->mask |= CM_SCACHEMASK_LENGTH;
2097     }
2098     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2099         /* really extending the file */
2100         scp->length = *sizep;
2101         scp->mask |= CM_SCACHEMASK_LENGTH;
2102     }
2103
2104     /* done successfully */
2105     code = 0;
2106
2107   done:
2108     lock_ReleaseMutex(&scp->mx);
2109     lock_ReleaseWrite(&scp->bufCreateLock);
2110
2111     return code;
2112 }
2113
2114 /* set the file size or other attributes (but not both at once) */
2115 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2116                 cm_req_t *reqp)
2117 {
2118     long code;
2119     int flags;
2120     AFSFetchStatus afsOutStatus;
2121     AFSVolSync volSync;
2122     cm_conn_t *connp;
2123     AFSFid tfid;
2124     AFSStoreStatus afsInStatus;
2125     struct rx_connection * callp;
2126
2127     /* handle file length setting */
2128     if (attrp->mask & CM_ATTRMASK_LENGTH)
2129         return cm_SetLength(scp, &attrp->length, userp, reqp);
2130
2131     flags = CM_SCACHESYNC_STORESTATUS;
2132
2133     lock_ObtainMutex(&scp->mx);
2134     /* otherwise, we have to make an RPC to get the status */
2135     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2136
2137     /* make the attr structure */
2138     cm_StatusFromAttr(&afsInStatus, scp, attrp);
2139
2140     tfid.Volume = scp->fid.volume;
2141     tfid.Vnode = scp->fid.vnode;
2142     tfid.Unique = scp->fid.unique;
2143
2144     lock_ReleaseMutex(&scp->mx);
2145     if (code) 
2146         return code;
2147
2148     /* now make the RPC */
2149     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2150     do {
2151         code = cm_Conn(&scp->fid, userp, reqp, &connp);
2152         if (code) 
2153             continue;
2154
2155         callp = cm_GetRxConn(connp);
2156         code = RXAFS_StoreStatus(callp, &tfid,
2157                                   &afsInStatus, &afsOutStatus, &volSync);
2158         rx_PutConnection(callp);
2159
2160     } while (cm_Analyze(connp, userp, reqp,
2161                          &scp->fid, &volSync, NULL, NULL, code));
2162     code = cm_MapRPCError(code, reqp);
2163
2164     if (code)
2165         osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2166     else
2167         osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2168
2169     lock_ObtainMutex(&scp->mx);
2170     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2171     if (code == 0)
2172         cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
2173                         CM_MERGEFLAG_FORCE);
2174         
2175     /* if we're changing the mode bits, discard the ACL cache, 
2176      * since we changed the mode bits.
2177      */
2178     if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
2179     lock_ReleaseMutex(&scp->mx);
2180     return code;
2181 }       
2182
2183 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2184                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2185 {       
2186     cm_conn_t *connp;
2187     long code;
2188     AFSFid dirAFSFid;
2189     cm_callbackRequest_t cbReq;
2190     AFSFid newAFSFid;
2191     cm_fid_t newFid;
2192     cm_scache_t *scp;
2193     int didEnd;
2194     AFSStoreStatus inStatus;
2195     AFSFetchStatus updatedDirStatus;
2196     AFSFetchStatus newFileStatus;
2197     AFSCallBack newFileCallback;
2198     AFSVolSync volSync;
2199     struct rx_connection * callp;
2200
2201     /* can't create names with @sys in them; must expand it manually first.
2202      * return "invalid request" if they try.
2203      */
2204     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2205         return CM_ERROR_ATSYS;
2206     }
2207
2208     /* before starting the RPC, mark that we're changing the file data, so
2209      * that someone who does a chmod will know to wait until our call
2210      * completes.
2211      */
2212     lock_ObtainMutex(&dscp->mx);
2213     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2214     if (code == 0) {
2215         cm_StartCallbackGrantingCall(NULL, &cbReq);
2216     }
2217     lock_ReleaseMutex(&dscp->mx);
2218     if (code) {
2219         return code;
2220     }
2221     didEnd = 0;
2222
2223     cm_StatusFromAttr(&inStatus, NULL, attrp);
2224
2225     /* try the RPC now */
2226     osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2227     do {
2228         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2229         if (code) 
2230             continue;
2231
2232         dirAFSFid.Volume = dscp->fid.volume;
2233         dirAFSFid.Vnode = dscp->fid.vnode;
2234         dirAFSFid.Unique = dscp->fid.unique;
2235
2236         callp = cm_GetRxConn(connp);
2237         code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2238                                  &inStatus, &newAFSFid, &newFileStatus,
2239                                  &updatedDirStatus, &newFileCallback,
2240                                  &volSync);
2241         rx_PutConnection(callp);
2242
2243     } while (cm_Analyze(connp, userp, reqp,
2244                          &dscp->fid, &volSync, NULL, &cbReq, code));
2245     code = cm_MapRPCError(code, reqp);
2246         
2247     if (code)
2248         osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2249     else
2250         osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2251
2252     lock_ObtainMutex(&dscp->mx);
2253     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2254     if (code == 0) {
2255         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2256     }
2257     lock_ReleaseMutex(&dscp->mx);
2258
2259     /* now try to create the file's entry, too, but be careful to 
2260      * make sure that we don't merge in old info.  Since we weren't locking
2261      * out any requests during the file's creation, we may have pretty old
2262      * info.
2263      */
2264     if (code == 0) {
2265         newFid.cell = dscp->fid.cell;
2266         newFid.volume = dscp->fid.volume;
2267         newFid.vnode = newAFSFid.Vnode;
2268         newFid.unique = newAFSFid.Unique;
2269         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2270         if (code == 0) {
2271             lock_ObtainMutex(&scp->mx);
2272             if (!cm_HaveCallback(scp)) {
2273                 cm_MergeStatus(scp, &newFileStatus, &volSync,
2274                                 userp, 0);
2275                 cm_EndCallbackGrantingCall(scp, &cbReq,
2276                                             &newFileCallback, 0);
2277                 didEnd = 1;     
2278             }       
2279             lock_ReleaseMutex(&scp->mx);
2280             *scpp = scp;
2281         }
2282     }
2283
2284     /* make sure we end things properly */
2285     if (!didEnd)
2286         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2287
2288     return code;
2289 }       
2290
2291 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2292 {
2293     long code;
2294
2295     lock_ObtainWrite(&scp->bufCreateLock);
2296     code = buf_CleanVnode(scp, userp, reqp);
2297     lock_ReleaseWrite(&scp->bufCreateLock);
2298     if (code == 0) {
2299         lock_ObtainMutex(&scp->mx);
2300         scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
2301                          | CM_SCACHEFLAG_OUTOFSPACE);
2302         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2303                           | CM_SCACHEMASK_CLIENTMODTIME
2304                           | CM_SCACHEMASK_LENGTH))
2305             code = cm_StoreMini(scp, userp, reqp);
2306         lock_ReleaseMutex(&scp->mx);
2307     }
2308     return code;
2309 }
2310
2311 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2312                  cm_user_t *userp, cm_req_t *reqp)
2313 {
2314     cm_conn_t *connp;
2315     long code;
2316     AFSFid dirAFSFid;
2317     cm_callbackRequest_t cbReq;
2318     AFSFid newAFSFid;
2319     cm_fid_t newFid;
2320     cm_scache_t *scp;
2321     int didEnd;
2322     AFSStoreStatus inStatus;
2323     AFSFetchStatus updatedDirStatus;
2324     AFSFetchStatus newDirStatus;
2325     AFSCallBack newDirCallback;
2326     AFSVolSync volSync;
2327     struct rx_connection * callp;
2328
2329     /* can't create names with @sys in them; must expand it manually first.
2330      * return "invalid request" if they try.
2331      */
2332     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2333         return CM_ERROR_ATSYS;
2334     }
2335
2336     /* before starting the RPC, mark that we're changing the directory
2337      * data, so that someone who does a chmod on the dir will wait until
2338      * our call completes.
2339      */
2340     lock_ObtainMutex(&dscp->mx);
2341     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2342     if (code == 0) {
2343         cm_StartCallbackGrantingCall(NULL, &cbReq);
2344     }
2345     lock_ReleaseMutex(&dscp->mx);
2346     if (code) {
2347         return code;
2348     }
2349     didEnd = 0;
2350
2351     cm_StatusFromAttr(&inStatus, NULL, attrp);
2352
2353     /* try the RPC now */
2354     osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2355     do {
2356         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2357         if (code) 
2358             continue;
2359
2360         dirAFSFid.Volume = dscp->fid.volume;
2361         dirAFSFid.Vnode = dscp->fid.vnode;
2362         dirAFSFid.Unique = dscp->fid.unique;
2363
2364         callp = cm_GetRxConn(connp);
2365         code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2366                               &inStatus, &newAFSFid, &newDirStatus,
2367                               &updatedDirStatus, &newDirCallback,
2368                               &volSync);
2369         rx_PutConnection(callp);
2370
2371     } while (cm_Analyze(connp, userp, reqp,
2372                          &dscp->fid, &volSync, NULL, &cbReq, code));
2373     code = cm_MapRPCError(code, reqp);
2374         
2375     if (code)
2376         osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2377     else
2378         osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2379
2380     lock_ObtainMutex(&dscp->mx);
2381     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2382     if (code == 0) {
2383         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2384     }
2385     lock_ReleaseMutex(&dscp->mx);
2386
2387     /* now try to create the new dir's entry, too, but be careful to 
2388      * make sure that we don't merge in old info.  Since we weren't locking
2389      * out any requests during the file's creation, we may have pretty old
2390      * info.
2391      */
2392     if (code == 0) {
2393         newFid.cell = dscp->fid.cell;
2394         newFid.volume = dscp->fid.volume;
2395         newFid.vnode = newAFSFid.Vnode;
2396         newFid.unique = newAFSFid.Unique;
2397         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2398         if (code == 0) {
2399             lock_ObtainMutex(&scp->mx);
2400             if (!cm_HaveCallback(scp)) {
2401                 cm_MergeStatus(scp, &newDirStatus, &volSync,
2402                                 userp, 0);
2403                 cm_EndCallbackGrantingCall(scp, &cbReq,
2404                                             &newDirCallback, 0);
2405                 didEnd = 1;             
2406             }
2407             lock_ReleaseMutex(&scp->mx);
2408             cm_ReleaseSCache(scp);
2409         }
2410     }
2411
2412     /* make sure we end things properly */
2413     if (!didEnd)
2414         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2415
2416     /* and return error code */
2417     return code;
2418 }       
2419
2420 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2421              cm_user_t *userp, cm_req_t *reqp)
2422 {
2423     cm_conn_t *connp;
2424     long code = 0;
2425     AFSFid dirAFSFid;
2426     AFSFid existingAFSFid;
2427     AFSFetchStatus updatedDirStatus;
2428     AFSFetchStatus newLinkStatus;
2429     AFSVolSync volSync;
2430     struct rx_connection * callp;
2431
2432     if (dscp->fid.cell != sscp->fid.cell ||
2433         dscp->fid.volume != sscp->fid.volume) {
2434         return CM_ERROR_CROSSDEVLINK;
2435     }
2436
2437     lock_ObtainMutex(&dscp->mx);
2438     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2439     lock_ReleaseMutex(&dscp->mx);
2440
2441     if (code)
2442         return code;
2443
2444     /* try the RPC now */
2445     osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2446     do {
2447         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2448         if (code) continue;
2449
2450         dirAFSFid.Volume = dscp->fid.volume;
2451         dirAFSFid.Vnode = dscp->fid.vnode;
2452         dirAFSFid.Unique = dscp->fid.unique;
2453
2454         existingAFSFid.Volume = sscp->fid.volume;
2455         existingAFSFid.Vnode = sscp->fid.vnode;
2456         existingAFSFid.Unique = sscp->fid.unique;
2457
2458         callp = cm_GetRxConn(connp);
2459         code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2460             &newLinkStatus, &updatedDirStatus, &volSync);
2461         rx_PutConnection(callp);
2462         osi_Log1(smb_logp,"  RXAFS_Link returns 0x%x", code);
2463
2464     } while (cm_Analyze(connp, userp, reqp,
2465         &dscp->fid, &volSync, NULL, NULL, code));
2466
2467     code = cm_MapRPCError(code, reqp);
2468
2469     if (code)
2470         osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2471     else
2472         osi_Log0(afsd_logp, "CALL Link SUCCESS");
2473
2474     lock_ObtainMutex(&dscp->mx);
2475     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2476     if (code == 0) {
2477         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2478     }
2479     lock_ReleaseMutex(&dscp->mx);
2480
2481     return code;
2482 }
2483
2484 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2485                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2486 {
2487     cm_conn_t *connp;
2488     long code;
2489     AFSFid dirAFSFid;
2490     AFSFid newAFSFid;
2491     cm_fid_t newFid;
2492     cm_scache_t *scp;
2493     AFSStoreStatus inStatus;
2494     AFSFetchStatus updatedDirStatus;
2495     AFSFetchStatus newLinkStatus;
2496     AFSVolSync volSync;
2497     struct rx_connection * callp;
2498
2499     /* before starting the RPC, mark that we're changing the directory data,
2500      * so that someone who does a chmod on the dir will wait until our
2501      * call completes.
2502      */
2503     lock_ObtainMutex(&dscp->mx);
2504     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2505     lock_ReleaseMutex(&dscp->mx);
2506     if (code) {
2507         return code;
2508     }
2509
2510     cm_StatusFromAttr(&inStatus, NULL, attrp);
2511
2512     /* try the RPC now */
2513     osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
2514     do {
2515         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2516         if (code) 
2517             continue;
2518
2519         dirAFSFid.Volume = dscp->fid.volume;
2520         dirAFSFid.Vnode = dscp->fid.vnode;
2521         dirAFSFid.Unique = dscp->fid.unique;
2522
2523         callp = cm_GetRxConn(connp);
2524         code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2525                               &inStatus, &newAFSFid, &newLinkStatus,
2526                               &updatedDirStatus, &volSync);
2527         rx_PutConnection(callp);
2528
2529     } while (cm_Analyze(connp, userp, reqp,
2530                          &dscp->fid, &volSync, NULL, NULL, code));
2531     code = cm_MapRPCError(code, reqp);
2532         
2533     if (code)
2534         osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
2535     else
2536         osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
2537
2538     lock_ObtainMutex(&dscp->mx);
2539     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2540     if (code == 0) {
2541         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2542     }
2543     lock_ReleaseMutex(&dscp->mx);
2544
2545     /* now try to create the new dir's entry, too, but be careful to 
2546      * make sure that we don't merge in old info.  Since we weren't locking
2547      * out any requests during the file's creation, we may have pretty old
2548      * info.
2549      */
2550     if (code == 0) {
2551         newFid.cell = dscp->fid.cell;
2552         newFid.volume = dscp->fid.volume;
2553         newFid.vnode = newAFSFid.Vnode;
2554         newFid.unique = newAFSFid.Unique;
2555         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2556         if (code == 0) {
2557             lock_ObtainMutex(&scp->mx);
2558             if (!cm_HaveCallback(scp)) {
2559                 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2560                                 userp, 0);
2561             }       
2562             lock_ReleaseMutex(&scp->mx);
2563             cm_ReleaseSCache(scp);
2564         }
2565     }
2566         
2567     /* and return error code */
2568     return code;
2569 }
2570
2571 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2572                    cm_req_t *reqp)
2573 {
2574     cm_conn_t *connp;
2575     long code;
2576     AFSFid dirAFSFid;
2577     int didEnd;
2578     AFSFetchStatus updatedDirStatus;
2579     AFSVolSync volSync;
2580     struct rx_connection * callp;
2581
2582     /* before starting the RPC, mark that we're changing the directory data,
2583      * so that someone who does a chmod on the dir will wait until our
2584      * call completes.
2585      */
2586     lock_ObtainMutex(&dscp->mx);
2587     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2588     lock_ReleaseMutex(&dscp->mx);
2589     if (code) {
2590         return code;
2591     }
2592     didEnd = 0;
2593
2594     /* try the RPC now */
2595     osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
2596     do {
2597         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2598         if (code) 
2599             continue;
2600
2601         dirAFSFid.Volume = dscp->fid.volume;
2602         dirAFSFid.Vnode = dscp->fid.vnode;
2603         dirAFSFid.Unique = dscp->fid.unique;
2604
2605         callp = cm_GetRxConn(connp);
2606         code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2607                                 &updatedDirStatus, &volSync);
2608         rx_PutConnection(callp);
2609
2610     } while (cm_Analyze(connp, userp, reqp,
2611                          &dscp->fid, &volSync, NULL, NULL, code));
2612     code = cm_MapRPCErrorRmdir(code, reqp);
2613
2614     if (code)
2615         osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
2616     else
2617         osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
2618
2619     lock_ObtainMutex(&dscp->mx);
2620     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2621     if (code == 0) {
2622         cm_dnlcRemove(dscp, namep); 
2623         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2624     }
2625     lock_ReleaseMutex(&dscp->mx);
2626
2627     /* and return error code */
2628     return code;
2629 }
2630
2631 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2632 {
2633     /* grab mutex on contents */
2634     lock_ObtainMutex(&scp->mx);
2635
2636     /* reset the prefetch info */
2637     scp->prefetch.base.LowPart = 0;             /* base */
2638     scp->prefetch.base.HighPart = 0;
2639     scp->prefetch.end.LowPart = 0;              /* and end */
2640     scp->prefetch.end.HighPart = 0;
2641
2642     /* release mutex on contents */
2643     lock_ReleaseMutex(&scp->mx);
2644
2645     /* we're done */
2646     return 0;
2647 }       
2648
2649 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2650                 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2651 {
2652     cm_conn_t *connp;
2653     long code;
2654     AFSFid oldDirAFSFid;
2655     AFSFid newDirAFSFid;
2656     int didEnd;
2657     AFSFetchStatus updatedOldDirStatus;
2658     AFSFetchStatus updatedNewDirStatus;
2659     AFSVolSync volSync;
2660     int oneDir;
2661     struct rx_connection * callp;
2662
2663     /* before starting the RPC, mark that we're changing the directory data,
2664      * so that someone who does a chmod on the dir will wait until our call
2665      * completes.  We do this in vnode order so that we don't deadlock,
2666      * which makes the code a little verbose.
2667      */
2668     if (oldDscp == newDscp) {
2669         /* check for identical names */
2670         if (strcmp(oldNamep, newNamep) == 0)
2671             return CM_ERROR_RENAME_IDENTICAL;
2672
2673         oneDir = 1;
2674         lock_ObtainMutex(&oldDscp->mx);
2675         cm_dnlcRemove(oldDscp, oldNamep);
2676         cm_dnlcRemove(oldDscp, newNamep);
2677         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2678                           CM_SCACHESYNC_STOREDATA);
2679         lock_ReleaseMutex(&oldDscp->mx);
2680     }
2681     else {
2682         /* two distinct dir vnodes */
2683         oneDir = 0;
2684         if (oldDscp->fid.cell != newDscp->fid.cell ||
2685              oldDscp->fid.volume != newDscp->fid.volume)
2686             return CM_ERROR_CROSSDEVLINK;
2687
2688         /* shouldn't happen that we have distinct vnodes for two
2689          * different files, but could due to deliberate attack, or
2690          * stale info.  Avoid deadlocks and quit now.
2691          */
2692         if (oldDscp->fid.vnode == newDscp->fid.vnode)
2693             return CM_ERROR_CROSSDEVLINK;
2694
2695         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2696             lock_ObtainMutex(&oldDscp->mx);
2697             cm_dnlcRemove(oldDscp, oldNamep);
2698             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2699                               CM_SCACHESYNC_STOREDATA);
2700             lock_ReleaseMutex(&oldDscp->mx);
2701             if (code == 0) {
2702                 lock_ObtainMutex(&newDscp->mx);
2703                 cm_dnlcRemove(newDscp, newNamep);
2704                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2705                                   CM_SCACHESYNC_STOREDATA);
2706                 lock_ReleaseMutex(&newDscp->mx);
2707                 if (code) {
2708                     /* cleanup first one */
2709                     lock_ObtainMutex(&newDscp->mx);
2710                     cm_SyncOpDone(oldDscp, NULL,
2711                                    CM_SCACHESYNC_STOREDATA);
2712                     lock_ReleaseMutex(&oldDscp->mx);
2713                 }       
2714             }
2715         }
2716         else {
2717             /* lock the new vnode entry first */
2718             lock_ObtainMutex(&newDscp->mx);
2719             cm_dnlcRemove(newDscp, newNamep);
2720             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2721                               CM_SCACHESYNC_STOREDATA);
2722             lock_ReleaseMutex(&newDscp->mx);
2723             if (code == 0) {
2724                 lock_ObtainMutex(&oldDscp->mx);
2725                 cm_dnlcRemove(oldDscp, oldNamep);
2726                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2727                                   CM_SCACHESYNC_STOREDATA);
2728                 lock_ReleaseMutex(&oldDscp->mx);
2729                 if (code) {
2730                     /* cleanup first one */
2731                     lock_ObtainMutex(&newDscp->mx);
2732                     cm_SyncOpDone(newDscp, NULL,
2733                                    CM_SCACHESYNC_STOREDATA);
2734                     lock_ReleaseMutex(&newDscp->mx);
2735                 }       
2736             }
2737         }
2738     }   /* two distinct vnodes */
2739
2740     if (code) {
2741         return code;
2742     }
2743     didEnd = 0;
2744
2745     /* try the RPC now */
2746     osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p", 
2747               oldDscp, newDscp);
2748     do {
2749         code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2750         if (code) 
2751             continue;
2752
2753         oldDirAFSFid.Volume = oldDscp->fid.volume;
2754         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2755         oldDirAFSFid.Unique = oldDscp->fid.unique;
2756         newDirAFSFid.Volume = newDscp->fid.volume;
2757         newDirAFSFid.Vnode = newDscp->fid.vnode;
2758         newDirAFSFid.Unique = newDscp->fid.unique;
2759
2760         callp = cm_GetRxConn(connp);
2761         code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2762                              &newDirAFSFid, newNamep,
2763                              &updatedOldDirStatus, &updatedNewDirStatus,
2764                              &volSync);
2765         rx_PutConnection(callp);
2766
2767     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2768                          &volSync, NULL, NULL, code));
2769     code = cm_MapRPCError(code, reqp);
2770         
2771     if (code)
2772         osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2773     else
2774         osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2775
2776     /* update the individual stat cache entries for the directories */
2777     lock_ObtainMutex(&oldDscp->mx);
2778     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2779     if (code == 0) {
2780         cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2781                         userp, 0);
2782     }
2783     lock_ReleaseMutex(&oldDscp->mx);
2784
2785     /* and update it for the new one, too, if necessary */
2786     if (!oneDir) {
2787         lock_ObtainMutex(&newDscp->mx);
2788         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2789         if (code == 0) {
2790             cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2791                             userp, 0);
2792         }
2793         lock_ReleaseMutex(&newDscp->mx);
2794     }
2795
2796     /* and return error code */
2797     return code;
2798 }
2799
2800 /* Byte range locks:
2801
2802    The OpenAFS Windows client has to fake byte range locks given no
2803    server side support for such locks.  This is implemented as keyed
2804    byte range locks on the cache manager.
2805
2806    Keyed byte range locks:
2807
2808    Each cm_scache_t structure keeps track of a list of keyed locks.
2809    The key for a lock identifies an owner of a set of locks (referred
2810    to as a client).  Each key is represented by a value.  The set of
2811    key values used within a specific cm_scache_t structure form a
2812    namespace that has a scope of just that cm_scache_t structure.  The
2813    same key value can be used with another cm_scache_t structure and
2814    correspond to a completely different client.  However it is
2815    advantageous for the SMB or IFS layer to make sure that there is a
2816    1-1 mapping between client and keys over all cm_scache_t objects.
2817
2818    Assume a client C has key Key(C) (although, since the scope of the
2819    key is a cm_scache_t, the key can be Key(C,S), where S is the
2820    cm_scache_t.  But assume a 1-1 relation between keys and clients).
2821    A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
2822    inclusive (a.k.a. [O,O+L-1]).  The function Key(x) is implemented
2823    through cm_generateKey() function for both SMB and IFS.
2824
2825    The list of locks for a cm_scache_t object S is maintained in
2826    S->fileLocks.  The cache manager will set a lock on the AFS file
2827    server in order to assert the locks in S->fileLocks.  If only
2828    shared locks are in place for S, then the cache manager will obtain
2829    a LockRead lock, while if there are any exclusive locks, it will
2830    obtain a LockWrite lock.  If the exclusive locks are all released
2831    while the shared locks remain, then the cache manager will
2832    downgrade the lock from LockWrite to LockRead.  Similarly, if an
2833    exclusive lock is obtained when only shared locks exist, then the
2834    cache manager will try to upgrade the lock from LockRead to
2835    LockWrite.
2836
2837    Each lock L owned by client C maintains a key L->key such that
2838    L->key == Key(C), the effective range defined by L->LOffset and
2839    L->LLength such that the range of bytes affected by the lock is
2840    (L->LOffset, +L->LLength), a type maintained in L->LockType which
2841    is either exclusive or shared.
2842
2843    Lock states:
2844
2845    A lock exists iff it is in S->fileLocks for some cm_scache_t
2846    S. Existing locks are in one of the following states: ACTIVE,
2847    WAITLOCK, WAITUNLOCK, LOST, DELETED.
2848
2849    The following sections describe each lock and the associated
2850    transitions.
2851
2852    1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
2853       the lock with the AFS file server.  This type of lock can be
2854       exercised by a client to read or write to the locked region (as
2855       the lock allows).
2856
2857       1.1 ACTIVE->LOST: When the AFS file server fails to extend a
2858         server lock that was required to assert the lock.  Before
2859         marking the lock as lost, the cache manager checks if the file
2860         has changed on the server.  If the file has not changed, then
2861         the cache manager will attempt to obtain a new server lock
2862         that is sufficient to assert the client side locks for the
2863         file.  If any of these fail, the lock is marked as LOST.
2864         Otherwise, it is left as ACTIVE.
2865
2866       1.2 ACTIVE->DELETED: Lock is released.
2867
2868    2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
2869       grants the lock but the lock is yet to be asserted with the AFS
2870       file server.  Once the file server grants the lock, the state
2871       will transition to an ACTIVE lock.
2872
2873       2.1 WAITLOCK->ACTIVE: The server granted the lock.
2874
2875       2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
2876         waiting.
2877
2878       2.3 WAITLOCK->LOST: One or more locks from this client were
2879         marked as LOST.  No further locks will be granted to this
2880         client until all lost locks are removed.
2881
2882    3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
2883       receives a request for a lock that conflicts with an existing
2884       ACTIVE or WAITLOCK lock.  The lock will be placed in the queue
2885       and will be granted at such time the conflicting locks are
2886       removed, at which point the state will transition to either
2887       WAITLOCK or ACTIVE.
2888
2889       3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed.  The
2890         current serverLock is sufficient to assert this lock, or a
2891         sufficient serverLock is obtained.
2892
2893       3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
2894         however the required serverLock is yet to be asserted with the
2895         server.
2896
2897       3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
2898         released.
2899
2900       3.5 WAITUNLOCK->LOST: One or more locks from this client were
2901         marked as LOST.  No further locks will be granted to this
2902         client until all lost locks are removed.
2903
2904    4. LOST: A lock L is LOST if the server lock that was required to
2905       assert the lock could not be obtained or if it could not be
2906       extended, or if other locks by the same client were LOST.
2907       Essentially, once a lock is LOST, the contract between the cache
2908       manager and that specific client is no longer valid.
2909
2910       The cache manager rechecks the server lock once every minute and
2911       extends it as appropriate.  If this is not done for 5 minutes,
2912       the AFS file server will release the lock (the 5 minute timeout
2913       is based on current file server code and is fairly arbitrary).
2914       Once released, the lock cannot be re-obtained without verifying
2915       that the contents of the file hasn't been modified since the
2916       time the lock was released.  Re-obtaining the lock without
2917       verifying this may lead to data corruption.  If the lock can not
2918       be obtained safely, then all active locks for the cm_scache_t
2919       are marked as LOST.
2920
2921       4.1 LOST->DELETED: The lock is released.
2922
2923    5. DELETED: The lock is no longer relevant.  Eventually, it will
2924       get removed from the cm_scache_t. In the meantime, it will be
2925       treated as if it does not exist.
2926
2927       5.1 DELETED->not exist: The lock is removed from the
2928         cm_scache_t.
2929
2930    The following are classifications of locks based on their state.
2931
2932    6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK.  These locks
2933       have been accepted by the cache manager, but may or may not have
2934       been granted back to the client.
2935
2936    7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
2937
2938    8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
2939
2940    Lock operation:
2941
2942    A client C can READ range (Offset,+Length) of a file represented by
2943    cm_scache_t S iff (1):
2944
2945    1. for all _a_ in (Offset,+Length), all of the following is true:
2946
2947        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
2948          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
2949          shared.
2950
2951        1.2 For each LOST lock L in S->fileLocks such that _a_ in
2952          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
2953          Key(C)
2954
2955        (When locks are lost on an cm_scache_t, all locks are lost.  By
2956        4.2 (below), if there is an exclusive LOST lock, then there
2957        can't be any overlapping ACTIVE locks.)
2958
2959    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
2960
2961    2. for all _a_ in (Offset,+Length), one of the following is true:
2962
2963        2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
2964          does not exist a LOST lock L such that _a_ in
2965          (L->LOffset,+L->LLength).
2966
2967        2.2 Byte _a_ of S is owned by C under lock L (as specified in
2968          1.2) AND L->LockType is exclusive.
2969
2970    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
2971
2972    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
2973       true:
2974
2975        3.1 If L->LockType is exclusive then there does NOT exist a
2976          ACCEPTED lock M in S->fileLocks such that _a_ in
2977          (M->LOffset,+M->LLength).
2978
2979          (If we count all QUEUED locks then we hit cases such as
2980          cascading waiting locks where the locks later on in the queue
2981          can be granted without compromising file integrity.  On the
2982          other hand if only ACCEPTED locks are considered, then locks
2983          that were received earlier may end up waiting for locks that
2984          were received later to be unlocked. The choice of ACCEPTED
2985          locks was made to mimic the Windows byte range lock
2986          semantics.)
2987
2988        3.2 If L->LockType is shared then for each ACCEPTED lock M in
2989          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
2990          M->LockType is shared.
2991
2992    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
2993
2994        4.1 M->key != Key(C)
2995
2996        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
2997          and (M->LOffset,+M->LLength) do not intersect.
2998
2999          (Note: If a client loses a lock, it loses all locks.
3000          Subsequently, it will not be allowed to obtain any more locks
3001          until all existing LOST locks that belong to the client are
3002          released.  Once all locks are released by a single client,
3003          there exists no further contract between the client and AFS
3004          about the contents of the file, hence the client can then
3005          proceed to obtain new locks and establish a new contract.
3006
3007          This doesn't quite work as you think it should, because most
3008          applications aren't built to deal with losing locks they
3009          thought they once had.  For now, we don't have a good
3010          solution to lost locks.
3011
3012          Also, for consistency reasons, we have to hold off on
3013          granting locks that overlap exclusive LOST locks.)
3014
3015    A client C can only unlock locks L in S->fileLocks which have
3016    L->key == Key(C).
3017
3018    The representation and invariants are as follows:
3019
3020    - Each cm_scache_t structure keeps:
3021
3022        - A queue of byte-range locks (cm_scache_t::fileLocks) which
3023          are of type cm_file_lock_t.
3024
3025        - A record of the highest server-side lock that has been
3026          obtained for this object (cm_scache_t::serverLock), which is
3027          one of (-1), LockRead, LockWrite.
3028
3029        - A count of ACCEPTED exclusive and shared locks that are in the
3030          queue (cm_scache_t::sharedLocks and
3031          cm_scache_t::exclusiveLocks)
3032
3033    - Each cm_file_lock_t structure keeps:
3034
3035        - The type of lock (cm_file_lock_t::LockType)
3036
3037        - The key associated with the lock (cm_file_lock_t::key)
3038
3039        - The offset and length of the lock (cm_file_lock_t::LOffset
3040          and cm_file_lock_t::LLength)
3041
3042        - The state of the lock.
3043
3044        - Time of issuance or last successful extension
3045
3046    Semantic invariants:
3047
3048        I1. The number of ACCEPTED locks in S->fileLocks are
3049            (S->sharedLocks + S->exclusiveLocks)
3050
3051    External invariants:
3052
3053        I3. S->serverLock is the lock that we have asserted with the
3054            AFS file server for this cm_scache_t.
3055
3056        I4. S->serverLock == LockRead iff there is at least one ACTIVE
3057            shared lock, but no ACTIVE exclusive locks.
3058
3059        I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3060            exclusive lock.
3061
3062        I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3063            M->key == L->key IMPLIES M is LOST or DELETED.
3064
3065    --asanka
3066  */
3067
3068 #define IS_LOCK_ACTIVE(lockp)     (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3069
3070 #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)
3071
3072 #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)
3073
3074 #define IS_LOCK_LOST(lockp)       (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3075
3076 #define IS_LOCK_DELETED(lockp)    (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3077
3078 /* unsafe */
3079 #define IS_LOCK_ACCEPTED(lockp)   (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3080
3081 /* unsafe */
3082 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3083
3084 /* unsafe */
3085 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3086
3087 /* unsafe */
3088 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3089
3090 #define SERVERLOCKS_ENABLED(scp) (!(((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks))
3091
3092 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3093 {
3094     afs_int64 int_begin;
3095     afs_int64 int_end;
3096
3097     int_begin = MAX(pos->offset, neg->offset);
3098     int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3099
3100     if(int_begin < int_end) {
3101         if(int_begin == pos->offset) {
3102             pos->length = pos->offset + pos->length - int_end;
3103             pos->offset = int_end;
3104         } else if(int_end == pos->offset + pos->length) {
3105             pos->length = int_begin - pos->offset;
3106         }
3107
3108         /* We only subtract ranges if the resulting range is
3109            contiguous.  If we try to support non-contigous ranges, we
3110            aren't actually improving performance. */
3111     }
3112 }
3113
3114 /* Called with scp->mx held.  Returns 0 if all is clear to read the
3115    specified range by the client identified by key.
3116  */
3117 long cm_LockCheckRead(cm_scache_t *scp, 
3118                       LARGE_INTEGER LOffset, 
3119                       LARGE_INTEGER LLength, 
3120                       cm_key_t key)
3121 {
3122 #ifndef ADVISORY_LOCKS
3123
3124     cm_file_lock_t *fileLock;
3125     osi_queue_t *q;
3126     long code = 0;
3127     cm_range_t range;
3128     int substract_ranges = FALSE;
3129
3130     range.offset = LOffset.QuadPart;
3131     range.length = LLength.QuadPart;
3132
3133     /*
3134
3135      1. for all _a_ in (Offset,+Length), all of the following is true:
3136
3137        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3138          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3139          shared.
3140
3141        1.2 For each LOST lock L in S->fileLocks such that _a_ in
3142          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3143          Key(C)
3144
3145     */
3146
3147     lock_ObtainRead(&cm_scacheLock);
3148
3149     for(q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3150         fileLock = 
3151             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3152
3153         if (INTERSECT_RANGE(range, fileLock->range)) {
3154             if(IS_LOCK_ACTIVE(fileLock)) {
3155                 if(fileLock->key == key) {
3156
3157                     /* If there is an active lock for this client, it
3158                        is safe to substract ranges.*/
3159                     cm_LockRangeSubtract(&range, &fileLock->range);
3160                     substract_ranges = TRUE;
3161                 } else {
3162                     if(fileLock->lockType != LockRead) {
3163                         code = CM_ERROR_LOCK_CONFLICT;
3164                         break;
3165                     }
3166
3167                     /* even if the entire range is locked for reading,
3168                        we still can't grant the lock at this point
3169                        because the client may have lost locks. That
3170                        is, unless we have already seen an active lock
3171                        belonging to the client, in which case there
3172                        can't be any lost locks for this client. */
3173                     if(substract_ranges)
3174                         cm_LockRangeSubtract(&range, &fileLock->range);
3175                 }
3176             } else if(IS_LOCK_LOST(fileLock) &&
3177                       (fileLock->key == key || fileLock->lockType == LockWrite)) {
3178                 code = CM_ERROR_BADFD;
3179                 break;
3180             }
3181         }
3182     }
3183
3184     lock_ReleaseRead(&cm_scacheLock);
3185
3186     osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3187               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3188
3189     return code;
3190
3191 #else
3192
3193     return 0;
3194
3195 #endif
3196 }
3197
3198 /* Called with scp->mx held.  Returns 0 if all is clear to write the
3199    specified range by the client identified by key.
3200  */
3201 long cm_LockCheckWrite(cm_scache_t *scp,
3202                        LARGE_INTEGER LOffset,
3203                        LARGE_INTEGER LLength,
3204                        cm_key_t key)
3205 {
3206 #ifndef ADVISORY_LOCKS
3207
3208     cm_file_lock_t *fileLock;
3209     osi_queue_t *q;
3210     long code = 0;
3211     cm_range_t range;
3212
3213     range.offset = LOffset.QuadPart;
3214     range.length = LLength.QuadPart;
3215
3216     /*
3217    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3218
3219    2. for all _a_ in (Offset,+Length), one of the following is true:
3220
3221        2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3222          lock L such that _a_ in (L->LOffset,+L->LLength).
3223
3224        2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3225          exclusive.
3226     */
3227
3228     lock_ObtainRead(&cm_scacheLock);
3229
3230     for(q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3231         fileLock = 
3232             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3233
3234         if(INTERSECT_RANGE(range, fileLock->range)) {
3235             if(IS_LOCK_ACTIVE(fileLock)) {
3236                 if(fileLock->key == key) {
3237                     if(fileLock->lockType == LockWrite) {
3238
3239                         /* if there is an active lock for this client, it
3240                            is safe to substract ranges */
3241                         cm_LockRangeSubtract(&range, &fileLock->range);
3242                     } else {
3243                         code = CM_ERROR_LOCK_CONFLICT;
3244                         break;
3245                     }
3246                 } else {
3247                     code = CM_ERROR_LOCK_CONFLICT;
3248                     break;
3249                 }
3250             } else if(IS_LOCK_LOST(fileLock)) {
3251                 code = CM_ERROR_BADFD;
3252                 break;
3253             }
3254         }
3255     }
3256
3257     lock_ReleaseRead(&cm_scacheLock);
3258
3259     osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3260               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3261
3262     return code;
3263
3264 #else
3265
3266     return 0;
3267
3268 #endif
3269 }
3270
3271 /* Forward dcl. */
3272 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3273
3274 /* Called with cm_scacheLock write locked */
3275 static cm_file_lock_t * cm_GetFileLock(void) {
3276     cm_file_lock_t * l;
3277
3278     l = (cm_file_lock_t *) cm_freeFileLocks;
3279     if(l) {
3280         osi_QRemove(&cm_freeFileLocks, &l->q);
3281     } else {
3282         l = malloc(sizeof(cm_file_lock_t));
3283         osi_assert(l);
3284     }
3285
3286     memset(l, 0, sizeof(cm_file_lock_t));
3287
3288     return l;
3289 }
3290
3291 /* Called with cm_scacheLock write locked */
3292 static void cm_PutFileLock(cm_file_lock_t *l) {
3293     osi_QAdd(&cm_freeFileLocks, &l->q);
3294 }
3295
3296 /* called with scp->mx held */
3297 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
3298              LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
3299              cm_key_t key,
3300              int allowWait, cm_user_t *userp, cm_req_t *reqp,
3301              cm_file_lock_t **lockpp)
3302 {
3303     long code = 0;
3304     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
3305     AFSFid tfid;
3306     AFSVolSync volSync;
3307     cm_conn_t *connp;
3308     cm_file_lock_t *fileLock;
3309     osi_queue_t *q;
3310     struct rx_connection * callp;
3311     cm_range_t range;
3312     int wait_unlock = FALSE;
3313
3314     osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
3315              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
3316     osi_Log3(afsd_logp, "... allowWait %d key 0x%x:%x", allowWait, 
3317              (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
3318
3319     /*
3320    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3321
3322    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3323       true:
3324
3325        3.1 If L->LockType is exclusive then there does NOT exist a
3326          ACCEPTED lock M in S->fileLocks such that _a_ in
3327          (M->LOffset,+M->LLength).
3328
3329        3.2 If L->LockType is shared then for each ACCEPTED lock M in
3330          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3331          M->LockType is shared.
3332
3333    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3334
3335        4.1 M->key != Key(C)
3336
3337        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3338          and (M->LOffset,+M->LLength) do not intersect.
3339     */
3340
3341     range.offset = LOffset.QuadPart;
3342     range.length = LLength.QuadPart;
3343
3344     lock_ObtainRead(&cm_scacheLock);
3345
3346     for(q = scp->fileLocksH; q; q = osi_QNext(q)) {
3347         fileLock =
3348             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3349
3350         if(IS_LOCK_LOST(fileLock)) {
3351             if (fileLock->key == key) {
3352                 code = CM_ERROR_BADFD;
3353                 break;
3354             } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
3355                 code = CM_ERROR_WOULDBLOCK;
3356                 wait_unlock = TRUE;
3357                 break;
3358             }
3359         }
3360
3361         /* we don't need to check for deleted locks here since deleted
3362            locks are dequeued from scp->fileLocks */
3363         if(IS_LOCK_ACCEPTED(fileLock) &&
3364            INTERSECT_RANGE(range, fileLock->range)) {
3365
3366             if((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
3367                 fileLock->lockType != LockRead) {
3368                 wait_unlock = TRUE;
3369                 code = CM_ERROR_WOULDBLOCK;
3370                 break;
3371             }
3372         }
3373     }
3374
3375     lock_ReleaseRead(&cm_scacheLock);
3376
3377     if(code == 0 && SERVERLOCKS_ENABLED(scp)) {
3378         if(Which == scp->serverLock ||
3379            (Which == LockRead && scp->serverLock == LockWrite)) {
3380
3381             /* we already have the lock we need */
3382             osi_Log3(afsd_logp, "   we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]", 
3383                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3384             code = 0; /* redundant */
3385
3386         } else if((scp->exclusiveLocks > 0) ||
3387                 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
3388
3389             /* We are already waiting for some other lock.  We should
3390                wait for the daemon to catch up instead of generating a
3391                flood of SetLock calls. */
3392             osi_Log3(afsd_logp, "   already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
3393                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3394             code = CM_ERROR_WOULDBLOCK;
3395
3396         } else {
3397             cm_fid_t cfid;
3398             int newLock;
3399
3400             if (scp->serverLock == LockRead && Which == LockWrite) {
3401             
3402                 /* We want to escalate the lock to a LockWrite.
3403                    Unfortunately that's not really possible without
3404                    letting go of the current lock.  But for now we do
3405                    it anyway. */
3406
3407                 osi_Log0(afsd_logp, "   attempting to UPGRADE from LockRead to LockWrite.");
3408
3409                 tfid.Volume = scp->fid.volume;
3410                 tfid.Vnode = scp->fid.vnode;
3411                 tfid.Unique = scp->fid.unique;
3412                 cfid = scp->fid;
3413
3414                 lock_ReleaseMutex(&scp->mx);
3415
3416                 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3417
3418                 do {
3419                     code = cm_Conn(&cfid, userp, reqp, &connp);
3420                     if (code) 
3421                         break;
3422
3423                     callp = cm_GetRxConn(connp);
3424                     code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3425                     rx_PutConnection(callp);
3426
3427                 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3428                                     NULL, NULL, code));
3429                 code = cm_MapRPCError(code, reqp);
3430
3431                 if (code)
3432                     osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
3433                 else
3434                     osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
3435         
3436                 lock_ObtainMutex(&scp->mx);
3437
3438                 if (code) {
3439                     /* We couldn't release the lock */
3440                     goto check_code;
3441                 } else {
3442                     scp->serverLock = -1;
3443                 }
3444             }
3445
3446             /* We need to obtain a server lock of type Which in order
3447                to assert this file lock */
3448             tfid.Volume = scp->fid.volume;
3449             tfid.Vnode = scp->fid.vnode;
3450             tfid.Unique = scp->fid.unique;
3451             cfid = scp->fid;
3452
3453 #ifndef AGGRESSIVE_LOCKS
3454             newLock = Which;
3455 #else
3456             newLock = LockWrite;
3457 #endif
3458             osi_Log3(afsd_logp, "CALL SetLock scp 0x%p from %d to %d", scp, (int) scp->serverLock, newLock);
3459
3460             lock_ReleaseMutex(&scp->mx);
3461
3462             do {
3463                 code = cm_Conn(&cfid, userp, reqp, &connp);
3464                 if (code) 
3465                     break;
3466
3467                 callp = cm_GetRxConn(connp);
3468                 code = RXAFS_SetLock(callp, &tfid, newLock,
3469                                      &volSync);
3470                 rx_PutConnection(callp);
3471
3472             } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3473                                 NULL, NULL, code));
3474
3475             code = cm_MapRPCError(code, reqp);
3476             
3477             if (code) {
3478                 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3479             } else {
3480                 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3481             }
3482
3483             if (code == CM_ERROR_WOULDBLOCK && newLock != Which) {
3484                 /* we wanted LockRead.  We tried LockWrite. Now try LockRead again */
3485                 newLock = Which;
3486
3487                 /* am I sane? */
3488                 osi_assert(newLock == LockRead);
3489                 
3490                 osi_Log3(afsd_logp, "CALL SetLock AGAIN scp 0x%p from %d to %d", 
3491                          scp, (int) scp->serverLock, newLock);
3492
3493                 do {
3494                     code = cm_Conn(&cfid, userp, reqp, &connp);
3495                     if (code) 
3496                         break;
3497
3498                     callp = cm_GetRxConn(connp);
3499                     code = RXAFS_SetLock(callp, &tfid, newLock,
3500                                          &volSync);
3501                     rx_PutConnection(callp);
3502
3503                 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3504                                     NULL, NULL, code));
3505
3506                 code = cm_MapRPCError(code, reqp);                
3507
3508                 if (code) {
3509                     osi_Log1(afsd_logp, "CALL SetLock FAILURE AGAIN, code 0x%x", code);
3510                 } else {
3511                     osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3512                 }
3513             }
3514
3515             lock_ObtainMutex(&scp->mx);
3516
3517             if(code == 0)
3518                 scp->serverLock = newLock;
3519             else {
3520                 if ((scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
3521                     scp->serverLock == -1) {
3522                     /* Oops. We lost the lock. */
3523                     cm_LockMarkSCacheLost(scp);
3524                 }
3525             }
3526         }
3527     } else if (code == 0) {     /* server locks not enabled */
3528         osi_Log0(afsd_logp,
3529                  "  Skipping server lock for scp");
3530     }
3531
3532  check_code:
3533
3534     if (code != 0) {
3535         /* Special case error translations
3536
3537            Applications don't expect certain errors from a
3538            LockFile/UnlockFile call.  We need to translate some error
3539            code to codes that apps expect and handle. */
3540
3541         /* We shouldn't actually need to handle this case since we
3542            simulate locks for RO scps anyway. */
3543         if (code == CM_ERROR_READONLY) {
3544             osi_Log0(afsd_logp, "   Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
3545             code = CM_ERROR_NOACCESS;
3546         }
3547     }
3548
3549     if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait)) {
3550
3551         lock_ObtainWrite(&cm_scacheLock);
3552         fileLock = cm_GetFileLock();
3553         lock_ReleaseWrite(&cm_scacheLock);
3554 #ifdef DEBUG
3555         fileLock->fid = scp->fid;
3556 #endif
3557         fileLock->key = key;
3558         fileLock->lockType = Which;
3559         cm_HoldUser(userp);
3560         fileLock->userp = userp;
3561         fileLock->range = range;
3562         fileLock->flags = (code == 0 ? 0 : 
3563                            ((wait_unlock)?
3564                             CM_FILELOCK_FLAG_WAITUNLOCK :
3565                             CM_FILELOCK_FLAG_WAITLOCK));
3566
3567         if (!SERVERLOCKS_ENABLED(scp))
3568             fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
3569
3570         fileLock->lastUpdate = (code == 0) ? time(NULL) : 0;
3571
3572         osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
3573
3574         lock_ObtainWrite(&cm_scacheLock);
3575         cm_HoldSCacheNoLock(scp);
3576         fileLock->scp = scp;
3577         osi_QAdd(&cm_allFileLocks, &fileLock->q);
3578         lock_ReleaseWrite(&cm_scacheLock);
3579
3580         if (code != 0) {
3581             *lockpp = fileLock;
3582         }
3583
3584         if (IS_LOCK_ACCEPTED(fileLock)) {
3585             if(Which == LockRead)
3586                 scp->sharedLocks++;
3587             else
3588                 scp->exclusiveLocks++;
3589         }
3590
3591         osi_Log2(afsd_logp, "cm_Lock Lock added 0x%p flags 0x%x", fileLock, fileLock->flags);
3592         osi_Log4(afsd_logp, "   scp[0x%p] exclusives[%d] shared[%d] serverLock[%d]",
3593                  scp, scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3594     }
3595
3596     return code;
3597 }
3598
3599 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
3600
3601 /* Called with scp->mx held */
3602 long cm_UnlockByKey(cm_scache_t * scp,
3603                     cm_key_t key,
3604                     int flags,
3605                     cm_user_t * userp,
3606                      cm_req_t * reqp)
3607 {
3608     long code = 0;
3609     AFSFid tfid;
3610     AFSVolSync volSync;
3611     cm_conn_t *connp;
3612     cm_file_lock_t *fileLock;
3613     osi_queue_t *q, *qn;
3614     struct rx_connection * callp;
3615     int n_unlocks = 0;
3616
3617     osi_Log3(afsd_logp, "cm_UnlockByKey scp 0x%p key 0x%x:%x",
3618              scp, (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
3619
3620     lock_ObtainWrite(&cm_scacheLock);
3621
3622     for(q = scp->fileLocksH; q; q = qn) {
3623         qn = osi_QNext(q);
3624
3625         fileLock = (cm_file_lock_t *)
3626             ((char *) q - offsetof(cm_file_lock_t, fileq));
3627
3628 #ifdef DEBUG
3629         osi_Log4(afsd_logp, "   Checking lock[0x%x] range[%d,+%d] type[%d]",
3630                 fileLock, (unsigned long) fileLock->range.offset, (unsigned long) fileLock->range.length,
3631                 fileLock->lockType);
3632         osi_Log3(afsd_logp, "     key[0x%x:%x] flags[0x%x]",
3633                  (unsigned long)(fileLock->key >> 32),
3634                  (unsigned long)(fileLock->key & 0xffffffff),
3635                  fileLock->flags);
3636
3637         if(cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
3638             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
3639             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3640                      fileLock->fid.cell,
3641                      fileLock->fid.volume,
3642                      fileLock->fid.vnode,
3643                      fileLock->fid.unique);
3644             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3645                      fileLock->scp->fid.cell,
3646                      fileLock->scp->fid.volume,
3647                      fileLock->scp->fid.vnode,
3648                      fileLock->scp->fid.unique);
3649             osi_assert(FALSE);
3650         }
3651 #endif
3652
3653         if (!IS_LOCK_DELETED(fileLock) &&
3654             cm_KeyEquals(fileLock->key, key, flags)) {
3655             osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
3656                     fileLock->range.offset,
3657                     fileLock->range.length,
3658                     fileLock->lockType);
3659
3660             if (scp->fileLocksT == q)
3661                 scp->fileLocksT = osi_QPrev(q);
3662             osi_QRemove(&scp->fileLocksH,q);
3663
3664             if(IS_LOCK_ACCEPTED(fileLock)) {
3665                 if(fileLock->lockType == LockRead)
3666                     scp->sharedLocks--;
3667                 else
3668                     scp->exclusiveLocks--;
3669             }
3670
3671             fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
3672
3673             cm_ReleaseUser(fileLock->userp);
3674             cm_ReleaseSCacheNoLock(scp);
3675
3676             fileLock->userp = NULL;
3677             fileLock->scp = NULL;
3678
3679             n_unlocks++;
3680         }
3681     }
3682
3683     lock_ReleaseWrite(&cm_scacheLock);
3684
3685     if(n_unlocks == 0) {
3686         osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
3687         osi_Log3(afsd_logp, "   Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
3688                  scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3689         
3690         return 0;
3691     }
3692
3693     osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
3694
3695     osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
3696     osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
3697
3698     if (!SERVERLOCKS_ENABLED(scp)) {
3699         osi_Log0(afsd_logp, "  Skipping server lock for scp");
3700         goto done;
3701     }
3702
3703     /* Ideally we would go through the rest of the locks to determine
3704      * if one or more locks that were formerly in WAITUNLOCK can now
3705      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
3706      * scp->sharedLocks accordingly.  However, the retrying of locks
3707      * in that manner is done cm_RetryLock() manually.
3708      */
3709
3710     if (scp->serverLock == LockWrite && scp->exclusiveLocks == 0 && scp->sharedLocks > 0) {
3711
3712         cm_fid_t cfid;
3713
3714         /* The serverLock should be downgraded to LockRead */
3715         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
3716
3717         tfid.Volume = scp->fid.volume;
3718         tfid.Vnode = scp->fid.vnode;
3719         tfid.Unique = scp->fid.unique;
3720         cfid = scp->fid;
3721
3722         lock_ReleaseMutex(&scp->mx);
3723
3724         osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3725
3726         do {
3727             code = cm_Conn(&cfid, userp, reqp, &connp);
3728             if (code) 
3729                 break;
3730
3731             callp = cm_GetRxConn(connp);
3732             code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3733             rx_PutConnection(callp);
3734             
3735         } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3736                             NULL, NULL, code));
3737         code = cm_MapRPCError(code, reqp);
3738
3739         if (code)
3740             osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
3741         else
3742             osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
3743         
3744         lock_ObtainMutex(&scp->mx);
3745
3746         if (code) {
3747             /* so we couldn't release it.  Just let the lock be for now */
3748             code = 0;
3749             goto done;
3750         } else {
3751             scp->serverLock = -1;
3752         }
3753
3754         tfid.Volume = scp->fid.volume;
3755         tfid.Vnode = scp->fid.vnode;
3756         tfid.Unique = scp->fid.unique;
3757         cfid = scp->fid;
3758
3759         osi_Log3(afsd_logp, "CALL SetLock scp 0x%p from %d to %d", scp, (int) scp->serverLock, LockRead);
3760
3761         lock_ReleaseMutex(&scp->mx);
3762
3763         do {
3764
3765             code = cm_Conn(&cfid, userp, reqp, &connp);
3766             if (code) 
3767                 break;
3768
3769             callp = cm_GetRxConn(connp);
3770             code = RXAFS_SetLock(callp, &tfid, LockRead,
3771                                  &volSync);
3772
3773             rx_PutConnection(callp);
3774             
3775         } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3776                             NULL, NULL, code));
3777
3778         if (code)
3779             osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3780         else {
3781             osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3782         }
3783
3784         lock_ObtainMutex(&scp->mx);
3785         
3786         if(code == 0)
3787             scp->serverLock = LockRead;
3788         else {
3789             if ((scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
3790                 (scp->serverLock == -1)) {
3791                 /* Oopsie */
3792                 cm_LockMarkSCacheLost(scp);
3793             }
3794         }
3795
3796         /* failure here has no bearing on the return value of
3797            cm_Unlock() */
3798         code = 0;
3799
3800     } else if(scp->serverLock != (-1) && scp->exclusiveLocks == 0 && scp->sharedLocks == 0) {
3801         cm_fid_t cfid;
3802
3803         /* The serverLock should be released entirely */
3804
3805         tfid.Volume = scp->fid.volume;
3806         tfid.Vnode = scp->fid.vnode;
3807         tfid.Unique = scp->fid.unique;
3808         cfid = scp->fid;
3809
3810         lock_ReleaseMutex(&scp->mx);
3811
3812         osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3813
3814         do {
3815             code = cm_Conn(&cfid, userp, reqp, &connp);
3816             if (code) 
3817                 break;
3818
3819             callp = cm_GetRxConn(connp);
3820             code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3821             rx_PutConnection(callp);
3822
3823         } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3824                             NULL, NULL, code));
3825         code = cm_MapRPCError(code, reqp);
3826
3827         if (code)
3828             osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
3829         else
3830             osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
3831         
3832         lock_ObtainMutex(&scp->mx);
3833
3834         if (code == 0)
3835             scp->serverLock = (-1);
3836     }
3837
3838  done:
3839
3840     osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
3841     osi_Log3(afsd_logp, "   Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
3842              scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3843
3844     return code;
3845 }
3846
3847 long cm_Unlock(cm_scache_t *scp, 
3848                unsigned char sLockType,
3849                LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
3850                cm_key_t key, 
3851                cm_user_t *userp, 
3852                cm_req_t *reqp)
3853 {
3854     long code = 0;
3855     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
3856     AFSFid tfid;
3857     AFSVolSync volSync;
3858     cm_conn_t *connp;
3859     cm_file_lock_t *fileLock;
3860     osi_queue_t *q;
3861     int release_userp = FALSE;
3862     struct rx_connection * callp;
3863
3864     osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
3865              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
3866     osi_Log2(afsd_logp, "... key 0x%x:%x",
3867              (unsigned long) (key >> 32), (unsigned long) (key & 0xffffffff));
3868
3869     lock_ObtainRead(&cm_scacheLock);
3870
3871     for(q = scp->fileLocksH; q; q = osi_QNext(q)) {
3872         fileLock = (cm_file_lock_t *)
3873             ((char *) q - offsetof(cm_file_lock_t, fileq));
3874
3875 #ifdef DEBUG
3876         if(cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
3877             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
3878             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3879                      fileLock->fid.cell,
3880                      fileLock->fid.volume,
3881                      fileLock->fid.vnode,
3882                      fileLock->fid.unique);
3883             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3884                      fileLock->scp->fid.cell,
3885                      fileLock->scp->fid.volume,
3886                      fileLock->scp->fid.vnode,
3887                      fileLock->scp->fid.unique);
3888             osi_assert(FALSE);
3889         }
3890 #endif
3891         if (!IS_LOCK_DELETED(fileLock) &&
3892             fileLock->key == key &&
3893             fileLock->range.offset == LOffset.QuadPart &&
3894             fileLock->range.length == LLength.QuadPart) {
3895             break;
3896         }
3897     }
3898
3899     if(!q) {
3900         osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
3901         
3902         lock_ReleaseRead(&cm_scacheLock);
3903
3904         /* The lock didn't exist anyway. *shrug* */
3905         return 0;
3906     }
3907
3908     /* discard lock record */
3909     if (scp->fileLocksT == q)
3910         scp->fileLocksT = osi_QPrev(q);
3911     osi_QRemove(&scp->fileLocksH, q);
3912
3913     lock_ReleaseRead(&cm_scacheLock);
3914
3915     /*
3916      * Don't delete it here; let the daemon delete it, to simplify
3917      * the daemon's traversal of the list.
3918      */
3919
3920     lock_ObtainWrite(&cm_scacheLock);
3921
3922     if(IS_LOCK_ACCEPTED(fileLock)) {
3923         if(fileLock->lockType == LockRead)
3924             scp->sharedLocks--;
3925         else
3926             scp->exclusiveLocks--;
3927     }
3928
3929     fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
3930     if (userp != NULL) {
3931         cm_ReleaseUser(fileLock->userp);
3932     } else {
3933         userp = fileLock->userp;
3934         release_userp = TRUE;
3935     }
3936     fileLock->userp = NULL;
3937     cm_ReleaseSCacheNoLock(scp);
3938     fileLock->scp = NULL;
3939     lock_ReleaseWrite(&cm_scacheLock);
3940
3941     if (!SERVERLOCKS_ENABLED(scp)) {
3942         osi_Log0(afsd_logp, "   Skipping server locks for scp");
3943         goto done;
3944     }
3945
3946     /* Ideally we would go through the rest of the locks to determine
3947      * if one or more locks that were formerly in WAITUNLOCK can now
3948      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
3949      * scp->sharedLocks accordingly.  However, the retrying of locks
3950      * in that manner is done cm_RetryLock() manually.
3951      */
3952
3953     if (scp->serverLock == LockWrite && scp->exclusiveLocks == 0 && scp->sharedLocks > 0) {
3954
3955         cm_fid_t cfid;
3956
3957         /* The serverLock should be downgraded to LockRead */
3958         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
3959
3960         tfid.Volume = scp->fid.volume;
3961         tfid.Vnode = scp->fid.vnode;
3962         tfid.Unique = scp->fid.unique;
3963         cfid = scp->fid;
3964
3965         lock_ReleaseMutex(&scp->mx);
3966
3967         osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3968
3969         do {
3970             code = cm_Conn(&cfid, userp, reqp, &connp);
3971             if (code) 
3972                 break;
3973
3974             callp = cm_GetRxConn(connp);
3975             code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3976             rx_PutConnection(callp);
3977             
3978         } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3979                             NULL, NULL, code));
3980
3981         code = cm_MapRPCError(code, reqp);
3982
3983         if (code)
3984             osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
3985         else
3986             osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
3987         
3988         lock_ObtainMutex(&scp->mx);
3989
3990         if (code) {
3991             /* so we couldn't release it.  Just let the lock be for now */
3992             code = 0;
3993             goto done;
3994         } else {
3995             scp->serverLock = -1;
3996         }
3997
3998         tfid.Volume = scp->fid.volume;
3999         tfid.Vnode = scp->fid.vnode;
4000         tfid.Unique = scp->fid.unique;
4001         cfid = scp->fid;
4002
4003         osi_Log3(afsd_logp, "CALL SetLock scp 0x%p from %d to %d", scp, (int) scp->serverLock, LockRead);
4004
4005         lock_ReleaseMutex(&scp->mx);
4006
4007         do {
4008
4009             code = cm_Conn(&cfid, userp, reqp, &connp);
4010             if (code) 
4011                 break;
4012
4013             callp = cm_GetRxConn(connp);
4014             code = RXAFS_SetLock(callp, &tfid, LockRead,
4015                                  &volSync);
4016
4017             rx_PutConnection(callp);
4018             
4019         } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4020                             NULL, NULL, code));
4021
4022         if (code)
4023             osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4024         else {
4025             osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4026         }
4027
4028         lock_ObtainMutex(&scp->mx);
4029         
4030         if(code == 0)
4031             scp->serverLock = LockRead;
4032         else {
4033             if ((scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4034                 (scp->serverLock == -1)) {
4035                 /* Oopsie */
4036                 cm_LockMarkSCacheLost(scp);
4037             }
4038         }
4039
4040         /* failure here has no bearing on the return value of
4041            cm_Unlock() */
4042         code = 0;
4043
4044     } else if(scp->serverLock != (-1) && scp->exclusiveLocks == 0 && scp->sharedLocks == 0) {
4045         cm_fid_t cfid;
4046
4047         /* The serverLock should be released entirely */
4048
4049         tfid.Volume = scp->fid.volume;
4050         tfid.Vnode = scp->fid.vnode;
4051         tfid.Unique = scp->fid.unique;
4052         cfid = scp->fid;
4053
4054         lock_ReleaseMutex(&scp->mx);
4055
4056         osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4057
4058         do {
4059             code = cm_Conn(&cfid, userp, reqp, &connp);
4060             if (code) 
4061                 break;
4062
4063             callp = cm_GetRxConn(connp);
4064             code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
4065             rx_PutConnection(callp);
4066
4067         } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4068                             NULL, NULL, code));
4069         code = cm_MapRPCError(code, reqp);
4070
4071         if (code)
4072             osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
4073         else
4074             osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
4075         
4076         lock_ObtainMutex(&scp->mx);
4077
4078         if (code == 0) {
4079             scp->serverLock = (-1);
4080         }
4081     }
4082
4083     if (release_userp)
4084         cm_ReleaseUser(userp);
4085
4086  done:
4087
4088     osi_Log4(afsd_logp, "cm_Unlock code 0x%x leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4089              code, scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4090
4091     return code;
4092 }
4093
4094 /* called with scp->mx held */
4095 static void cm_LockMarkSCacheLost(cm_scache_t * scp)
4096 {
4097     cm_file_lock_t *fileLock;
4098     osi_queue_t *q;
4099
4100     osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
4101
4102 #ifdef DEBUG
4103     /* With the current code, we can't lose a lock on a RO scp */
4104     osi_assert(!(scp->flags & CM_SCACHEFLAG_RO));
4105 #endif
4106
4107     /* cm_scacheLock needed because we are modifying fileLock->flags */
4108     lock_ObtainWrite(&cm_scacheLock);
4109
4110     for(q = scp->fileLocksH; q; q = osi_QNext(q)) {
4111         fileLock = 
4112             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4113
4114         if(IS_LOCK_ACTIVE(fileLock)) {
4115             if (fileLock->lockType == LockRead)
4116                 scp->sharedLocks--;
4117             else
4118                 scp->exclusiveLocks--;
4119
4120             fileLock->flags |= CM_FILELOCK_FLAG_LOST;
4121         }
4122     }
4123
4124     scp->serverLock = -1;
4125     lock_ReleaseWrite(&cm_scacheLock);
4126 }
4127
4128 /* Called with no relevant locks held */
4129 void cm_CheckLocks()
4130 {
4131     osi_queue_t *q, *nq;
4132     cm_file_lock_t *fileLock;
4133     cm_req_t req;
4134     AFSFid tfid;
4135     AFSVolSync volSync;
4136     cm_conn_t *connp;
4137     long code;
4138     struct rx_connection * callp;
4139     cm_scache_t * scp;
4140
4141     cm_InitReq(&req);
4142
4143     lock_ObtainWrite(&cm_scacheLock);
4144
4145     cm_lockRefreshCycle++;
4146
4147     osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
4148
4149     for(q = cm_allFileLocks; q; q = nq) {
4150         fileLock = (cm_file_lock_t *) q;
4151         nq = osi_QNext(q);
4152         code = -1;
4153
4154         if (IS_LOCK_DELETED(fileLock)) {
4155
4156             osi_QRemove(&cm_allFileLocks, q);
4157             cm_PutFileLock(fileLock);
4158
4159         } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
4160
4161             /* Server locks must have been enabled for us to have
4162                received an active non-client-only lock. */
4163             //osi_assert(cm_enableServerLocks);
4164
4165             scp = fileLock->scp;
4166             osi_assert(scp != NULL);
4167
4168             cm_HoldSCacheNoLock(scp);
4169
4170 #ifdef DEBUG
4171             if(cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4172                 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4173                 osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4174                          fileLock->fid.cell,
4175                          fileLock->fid.volume,
4176                          fileLock->fid.vnode,
4177                          fileLock->fid.unique);
4178                 osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4179                          fileLock->scp->fid.cell,
4180                          fileLock->scp->fid.volume,
4181                          fileLock->scp->fid.vnode,
4182                          fileLock->scp->fid.unique);
4183                 osi_assert(FALSE);
4184             }
4185 #endif
4186             /* Server locks are extended once per scp per refresh
4187                cycle. */
4188             if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
4189
4190                 int scp_done = FALSE;
4191
4192                 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
4193
4194                 lock_ReleaseWrite(&cm_scacheLock);
4195                 lock_ObtainMutex(&scp->mx);
4196
4197                 /* did the lock change while we weren't holding the lock? */
4198                 if (!IS_LOCK_ACTIVE(fileLock))
4199                     goto post_syncopdone;
4200
4201                 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
4202                                  CM_SCACHESYNC_NEEDCALLBACK
4203                                  | CM_SCACHESYNC_GETSTATUS
4204                                  | CM_SCACHESYNC_LOCK);
4205
4206                 if (code) {
4207                     osi_Log1(smb_logp, "cm_CheckLocks SyncOp failure code 0x%x", code);
4208                     goto post_syncopdone;
4209                 }
4210
4211                 /* cm_SyncOp releases scp->mx during which the lock
4212                    may get released. */
4213                 if (!IS_LOCK_ACTIVE(fileLock))
4214                     goto pre_syncopdone;
4215
4216                 if(scp->serverLock != -1) {
4217                     cm_fid_t cfid;
4218                     cm_user_t * userp;
4219
4220                     tfid.Volume = scp->fid.volume;
4221                     tfid.Vnode = scp->fid.vnode;
4222                     tfid.Unique = scp->fid.unique;
4223                     cfid = scp->fid;
4224                     userp = fileLock->userp;
4225                     
4226                     osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d", 
4227                              fileLock,
4228                              scp,
4229                              (int) scp->serverLock);
4230
4231                     lock_ReleaseMutex(&scp->mx);
4232
4233                     do {
4234                         code = cm_Conn(&cfid, userp,
4235                                        &req, &connp);
4236                         if (code) 
4237                             break;
4238
4239                         callp = cm_GetRxConn(connp);
4240                         code = RXAFS_ExtendLock(callp, &tfid,
4241                                                 &volSync);
4242                         rx_PutConnection(callp);
4243
4244                         osi_Log1(afsd_logp, "   ExtendLock returns %d", code);
4245
4246                     } while (cm_Analyze(connp, userp, &req,
4247                                         &cfid, &volSync, NULL, NULL,
4248                                         code));
4249
4250                     code = cm_MapRPCError(code, &req);
4251
4252                     lock_ObtainMutex(&scp->mx);
4253
4254                     if (code) {
4255                         osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
4256                         if (code == EINVAL || code == CM_ERROR_INVAL)
4257                             cm_LockMarkSCacheLost(scp);
4258                     } else {
4259                         osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
4260                     }
4261                 } else {
4262                     /* interestingly, we have found an active lock
4263                        belonging to an scache that has no
4264                        serverLock */
4265                     cm_LockMarkSCacheLost(scp);
4266                 }
4267
4268                 scp_done = TRUE;
4269
4270             pre_syncopdone:
4271
4272                 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
4273
4274             post_syncopdone:
4275                 lock_ReleaseMutex(&scp->mx);
4276
4277                 lock_ObtainWrite(&cm_scacheLock);
4278
4279                 if (code == 0) {
4280                     fileLock->lastUpdate = time(NULL);
4281                 }
4282                 
4283                 if (scp_done)
4284                     scp->lastRefreshCycle = cm_lockRefreshCycle;
4285
4286             } else {
4287                 /* we have already refreshed the locks on this scp */
4288                 fileLock->lastUpdate = time(NULL);
4289             }
4290
4291             cm_ReleaseSCacheNoLock(scp);
4292
4293         } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
4294             /* TODO: Check callbacks */
4295         }
4296     }
4297
4298     lock_ReleaseWrite(&cm_scacheLock);
4299     osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
4300 }
4301
4302 /* NOT called with scp->mx held. */
4303 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
4304 {
4305     long code = 0;
4306     cm_scache_t *scp = NULL;
4307     AFSFid tfid;
4308     AFSVolSync volSync;
4309     cm_conn_t *connp;
4310     cm_file_lock_t *fileLock;
4311     osi_queue_t *q;
4312     cm_req_t req;
4313     struct rx_connection * callp;
4314     int newLock = -1;
4315
4316     cm_InitReq(&req);
4317
4318     if (client_is_dead) {
4319         code = CM_ERROR_TIMEDOUT;
4320         goto updateLock;
4321     }
4322
4323     lock_ObtainRead(&cm_scacheLock);
4324
4325     osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
4326     osi_Log4(afsd_logp, "    offset(%x:%x) length(%x:%x)",
4327              (unsigned)(oldFileLock->range.offset >> 32),
4328              (unsigned)(oldFileLock->range.offset & 0xffffffff),
4329              (unsigned)(oldFileLock->range.length >> 32),
4330              (unsigned)(oldFileLock->range.length & 0xffffffff));
4331     osi_Log3(afsd_logp, "    key(%x:%x) flags=%x",
4332              (unsigned)(oldFileLock->key >> 32),
4333              (unsigned)(oldFileLock->key & 0xffffffff),
4334              (unsigned)(oldFileLock->flags));
4335
4336     /* if the lock has already been granted, then we have nothing to do */
4337     if(IS_LOCK_ACTIVE(oldFileLock)) {
4338         lock_ReleaseRead(&cm_scacheLock);
4339         osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
4340         return 0;
4341     }
4342
4343     /* we can't do anything with lost or deleted locks at the moment. */
4344     if(IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
4345         code = CM_ERROR_BADFD;
4346         osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
4347         lock_ReleaseRead(&cm_scacheLock);
4348         goto updateLock;
4349     }
4350
4351     scp = oldFileLock->scp;
4352
4353     osi_assert(scp != NULL);
4354
4355     lock_ReleaseRead(&cm_scacheLock);
4356     lock_ObtainMutex(&scp->mx);
4357     lock_ObtainWrite(&cm_scacheLock);
4358
4359     /* Check if we already have a sufficient server lock to allow this
4360        lock to go through */
4361     if(IS_LOCK_WAITLOCK(oldFileLock) &&
4362        (!SERVERLOCKS_ENABLED(scp) ||
4363         scp->serverLock == oldFileLock->lockType ||
4364         scp->serverLock == LockWrite)) {
4365
4366         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4367
4368         if (SERVERLOCKS_ENABLED(scp)) {
4369             osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock.  Granting",
4370                      (int) scp->serverLock);
4371         } else {
4372             osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
4373         }
4374
4375         lock_ReleaseWrite(&cm_scacheLock);
4376         lock_ReleaseMutex(&scp->mx);
4377
4378         return 0;
4379     }
4380
4381     if(IS_LOCK_WAITUNLOCK(oldFileLock)) {
4382
4383         /* check if the conflicting locks have dissappeared already */
4384
4385         for(q = scp->fileLocksH; q; q = osi_QNext(q)) {
4386
4387             fileLock = (cm_file_lock_t *)
4388                 ((char *) q - offsetof(cm_file_lock_t, fileq));
4389
4390             if(IS_LOCK_LOST(fileLock)) {
4391                 if (fileLock->key == oldFileLock->key) {
4392                     code = CM_ERROR_BADFD;
4393                     oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
4394                     osi_Log1(afsd_logp, "    found lost lock %p for same key.  Marking lock as lost",
4395                              fileLock);
4396                     break;
4397                 } else if (fileLock->lockType == LockWrite &&
4398                            INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
4399                     osi_Log1(afsd_logp, "    found conflicting LOST lock %p", fileLock);
4400                     code = CM_ERROR_WOULDBLOCK;
4401                     break;
4402                 }
4403             }
4404
4405             if(IS_LOCK_ACCEPTED(fileLock) &&
4406                INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
4407
4408                 if(oldFileLock->lockType != LockRead ||
4409                    fileLock->lockType != LockRead) {
4410
4411                     osi_Log1(afsd_logp, "    found conflicting lock %p", fileLock);
4412                     code = CM_ERROR_WOULDBLOCK;
4413                     break;
4414                 }
4415             }
4416         }
4417     }
4418
4419     if (code != 0) {
4420         lock_ReleaseWrite(&cm_scacheLock);
4421         lock_ReleaseMutex(&scp->mx);
4422
4423         goto handleCode;
4424     }
4425
4426     /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
4427        If it is WAITUNLOCK, then we didn't find any conflicting lock
4428        but we haven't verfied whether the serverLock is sufficient to
4429        assert it.  If it is WAITLOCK, then the serverLock is
4430        insufficient to assert it. Eitherway, we are ready to accept
4431        the lock as either ACTIVE or WAITLOCK depending on the
4432        serverLock. */
4433
4434     /* First, promote the WAITUNLOCK to a WAITLOCK */
4435     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
4436         if (oldFileLock->lockType == LockRead)
4437             scp->sharedLocks++;
4438         else
4439             scp->exclusiveLocks++;
4440
4441         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
4442         oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
4443     }
4444
4445     if (!SERVERLOCKS_ENABLED(scp) ||
4446         scp->serverLock == oldFileLock->lockType ||
4447         (oldFileLock->lockType == LockRead &&
4448          scp->serverLock == LockWrite)) {
4449
4450         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4451
4452         lock_ReleaseWrite(&cm_scacheLock);
4453         lock_ReleaseMutex(&scp->mx);
4454
4455         return 0;
4456
4457     } else {
4458         cm_fid_t cfid;
4459         cm_user_t * userp;
4460
4461         code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
4462                          CM_SCACHESYNC_NEEDCALLBACK
4463                          | CM_SCACHESYNC_GETSTATUS
4464                          | CM_SCACHESYNC_LOCK);
4465         if (code) {
4466             osi_Log1(smb_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
4467             lock_ReleaseWrite(&cm_scacheLock);
4468             goto post_syncopdone;
4469         }
4470
4471         if(!IS_LOCK_WAITLOCK(oldFileLock))
4472             goto pre_syncopdone;
4473
4474         tfid.Volume = scp->fid.volume;
4475         tfid.Vnode = scp->fid.vnode;
4476         tfid.Unique = scp->fid.unique;
4477         cfid = scp->fid;
4478         userp = oldFileLock->userp;
4479
4480 #ifndef AGGRESSIVE_LOCKS
4481         newLock = oldFileLock->lockType;
4482 #else
4483         newLock = LockWrite;
4484 #endif
4485
4486         osi_Log1(afsd_logp, "CALL SetLock lock 0x%p", oldFileLock);
4487
4488         lock_ReleaseWrite(&cm_scacheLock);
4489         lock_ReleaseMutex(&scp->mx);
4490
4491         do {
4492             code = cm_Conn(&cfid, userp, &req, &connp);
4493             if (code) 
4494                 break;
4495
4496             callp = cm_GetRxConn(connp);
4497             code = RXAFS_SetLock(callp, &tfid, newLock,
4498                                   &volSync);
4499             rx_PutConnection(callp);
4500
4501         } while (cm_Analyze(connp, userp, &req,
4502                              &cfid, &volSync,
4503                              NULL, NULL, code));
4504         code = cm_MapRPCError(code, &req);
4505
4506         if (code) {
4507             osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4508         } else {
4509             osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4510         }
4511
4512         lock_ObtainMutex(&scp->mx);
4513     pre_syncopdone:
4514         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
4515     post_syncopdone:
4516         ;
4517     }
4518
4519   handleCode:
4520     if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
4521         if (scp->fileLocksT == &oldFileLock->fileq)
4522             scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
4523         osi_QRemove(&scp->fileLocksH, &oldFileLock->fileq);
4524     } else if (code == 0 && IS_LOCK_WAITLOCK(oldFileLock)) {
4525         scp->serverLock = newLock;
4526     }
4527     lock_ReleaseMutex(&scp->mx);
4528
4529   updateLock:
4530     lock_ObtainWrite(&cm_scacheLock);
4531     if (code == 0) {
4532         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4533     } else if (code != CM_ERROR_WOULDBLOCK) {
4534         oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4535         cm_ReleaseUser(oldFileLock->userp);
4536         oldFileLock->userp = NULL;
4537         if (oldFileLock->scp) {
4538             cm_ReleaseSCacheNoLock(oldFileLock->scp);
4539             oldFileLock->scp = NULL;
4540         }
4541     }
4542     lock_ReleaseWrite(&cm_scacheLock);
4543
4544     return code;
4545 }
4546
4547 cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
4548 {
4549     return (((cm_key_t) process_id) << 32) |
4550         (((cm_key_t) session_id) << 16) |
4551         (((cm_key_t) file_id));
4552 }
4553
4554 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags)
4555 {
4556     if (flags & CM_UNLOCK_BY_FID) {
4557         return ((k1 & 0xffffffff) == (k2 & 0xffffffff));
4558     } else {
4559         return (k1 == k2);
4560     }
4561 }
4562
4563 void cm_ReleaseAllLocks(void)
4564 {
4565     cm_scache_t *scp;
4566     cm_req_t req;
4567     cm_user_t *userp;
4568     cm_key_t   key;
4569     cm_file_lock_t *fileLock;
4570     int i;
4571
4572     for (i = 0; i < cm_data.hashTableSize; i++)
4573     {
4574         for ( scp = cm_data.hashTablep[i]; scp; scp = scp->nextp ) {
4575             while (scp->fileLocksH != NULL) {
4576                 lock_ObtainMutex(&scp->mx);
4577                 lock_ObtainWrite(&cm_scacheLock);
4578                 if (!scp->fileLocksH) {
4579                     lock_ReleaseWrite(&cm_scacheLock);
4580                     lock_ReleaseMutex(&scp->mx);
4581                     break;
4582                 }
4583                 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
4584                 userp = fileLock->userp;
4585                 cm_HoldUser(userp);
4586                 key = fileLock->key;
4587                 cm_HoldSCacheNoLock(scp);
4588                 lock_ReleaseWrite(&cm_scacheLock);
4589                 cm_UnlockByKey(scp, key, 0, userp, &req);
4590                 cm_ReleaseSCache(scp);
4591                 cm_ReleaseUser(userp);
4592                 lock_ReleaseMutex(&scp->mx);
4593             }
4594         }
4595     }
4596 }