91219c16fd5bd707c030ac87d9be0c4b6689a559
[openafs.git] / src / WINNT / afsd / cm_vnodeops.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afs/param.h>
11 #include <afs/stds.h>
12
13 #ifndef DJGPP
14 #include <windows.h>
15 #include <winsock2.h>
16 #endif /* !DJGPP */
17 #include <stddef.h>
18 #include <malloc.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <errno.h>
22
23 #include <osi.h>
24
25 #include "afsd.h"
26
27 /* Used by cm_FollowMountPoint */
28 #define RWVOL   0
29 #define ROVOL   1
30 #define BACKVOL 2
31
32 #ifdef DEBUG
33 extern void afsi_log(char *pattern, ...);
34 #endif
35
36 int cm_enableServerLocks = 1;
37
38 /*
39  * Case-folding array.  This was constructed by inspecting of SMBtrace output.
40  * I do not know anything more about it.
41  */
42 unsigned char cm_foldUpper[256] = {
43      0x0,  0x1,  0x2,  0x3,  0x4,  0x5,  0x6,  0x7,
44      0x8,  0x9,  0xa,  0xb,  0xc,  0xd,  0xe,  0xf,
45     0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
46     0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
47     0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
48     0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
49     0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
50     0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
51     0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
52     0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
53     0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
54     0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
55     0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
56     0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
57     0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
58     0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
59     0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
60     0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
61     0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
62     0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
63     0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
64     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
65     0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
66     0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
67     0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
68     0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
69     0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
70     0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
71     0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
72     0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
73     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
74     0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
75 };
76
77 /*
78  * Case-insensitive string comparison.  We used to use stricmp, but it doesn't
79  * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
80  * upper-case u-umlaut).
81  */
82 int cm_stricmp(const char *str1, const char *str2)
83 {
84     char c1, c2;
85
86     while (1) {
87         if (*str1 == 0)
88             if (*str2 == 0)
89                 return 0;
90             else
91                 return -1;
92         if (*str2 == 0)
93             return 1;
94         c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
95         c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
96         if (c1 < c2)
97             return -1;
98         if (c1 > c2)
99             return 1;
100     }
101 }
102
103 /* characters that are legal in an 8.3 name */
104 /*
105  * We used to have 1's for all characters from 128 to 254.  But
106  * the NT client behaves better if we create an 8.3 name for any
107  * name that has a character with the high bit on, and if we
108  * delete those characters from 8.3 names.  In particular, see
109  * Sybase defect 10859.
110  */
111 char cm_LegalChars[256] = {
112  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
113  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
114  0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,
115  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
116  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
117  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
118  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
119  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
120  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
122  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
125  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
126  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
128 };
129
130 /* return true iff component is a valid 8.3 name */
131 int cm_Is8Dot3(char *namep)
132 {
133     int sawDot = 0;
134     unsigned char tc;
135     int charCount = 0;
136         
137     /*
138      * can't have a leading dot;
139      * special case for . and ..
140      */
141     if (namep[0] == '.') {
142         if (namep[1] == 0)
143             return 1;
144         if (namep[1] == '.' && namep[2] == 0)
145             return 1;
146         return 0;
147     }
148     while (tc = *namep++) {
149         if (tc == '.') {
150             /* saw another dot */
151             if (sawDot) return 0;       /* second dot */
152             sawDot = 1;
153             charCount = 0;
154             continue;
155         }
156         if (cm_LegalChars[tc] == 0)
157             return 0;
158         charCount++;
159         if (!sawDot && charCount > 8)
160             /* more than 8 chars in name */
161             return 0;
162         if (sawDot && charCount > 3)
163             /* more than 3 chars in extension */
164             return 0;
165     }
166     return 1;
167 }
168
169 /*
170  * Number unparsing map for generating 8.3 names;
171  * The version taken from DFS was on drugs.  
172  * You can't include '&' and '@' in a file name.
173  */
174 char cm_8Dot3Mapping[42] =
175 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
176  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 
177  'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 
178  'V', 'W', 'X', 'Y', 'Z', '_', '-', '$', '#', '!', '+', '='
179 };
180 int cm_8Dot3MapSize = sizeof(cm_8Dot3Mapping);
181
182 void cm_Gen8Dot3Name(cm_dirEntry_t *dep, char *shortName, char **shortNameEndp)
183 {
184     char number[12];
185     int i, nsize = 0;
186     int vnode = ntohl(dep->fid.vnode);
187     char *lastDot;
188     int validExtension = 0;
189     char tc, *temp, *name;
190
191     /* Unparse the file's vnode number to get a "uniquifier" */
192     do {
193         number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
194         nsize++;
195         vnode /= cm_8Dot3MapSize;
196     } while (vnode);
197
198     /*
199      * Look for valid extension.  There has to be a dot, and
200      * at least one of the characters following has to be legal.
201      */
202     lastDot = strrchr(dep->name, '.');
203     if (lastDot) {
204         temp = lastDot; temp++;
205         while (tc = *temp++)
206             if (cm_LegalChars[tc])
207                 break;
208         if (tc)
209             validExtension = 1;
210     }       
211
212     /* Copy name characters */
213     name = dep->name;
214     for (i = 0, name = dep->name;
215           i < (7 - nsize) && name != lastDot; ) {
216         tc = *name++;
217
218         if (tc == 0)
219             break;
220         if (!cm_LegalChars[tc])
221             continue;
222         i++;
223         *shortName++ = toupper(tc);
224     }
225
226     /* tilde */
227     *shortName++ = '~';
228
229     /* Copy uniquifier characters */
230     memcpy(shortName, number, nsize);
231     shortName += nsize;
232
233     if (validExtension) {
234         /* Copy extension characters */
235         *shortName++ = *lastDot++;      /* copy dot */
236         for (i = 0, tc = *lastDot++;
237               i < 3 && tc;
238               tc = *lastDot++) {
239             if (cm_LegalChars[tc]) {
240                 i++;
241                 *shortName++ = toupper(tc);
242             }
243         }
244     }
245
246     /* Trailing null */
247     *shortName = 0;
248
249     if (shortNameEndp)
250         *shortNameEndp = shortName;
251 }       
252
253 /* return success if we can open this file in this mode */
254 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
255                   cm_req_t *reqp)
256 {
257     long rights;
258     long code;
259
260     rights = 0;
261     if (openMode != 1) rights |= PRSFS_READ;
262     if (openMode == 1 || openMode == 2 || trunc) rights |= PRSFS_WRITE;
263         
264     lock_ObtainMutex(&scp->mx);
265
266     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
267                       CM_SCACHESYNC_GETSTATUS
268                       | CM_SCACHESYNC_NEEDCALLBACK);
269
270     if (code == 0 && 
271         ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
272         scp->fileType == CM_SCACHETYPE_FILE) {
273
274         cm_key_t key;
275         unsigned int sLockType;
276         LARGE_INTEGER LOffset, LLength;
277
278         /* Check if there's some sort of lock on the file at the
279            moment. */
280
281         key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
282
283         if (rights & PRSFS_WRITE)
284             sLockType = 0;
285         else
286             sLockType = LOCKING_ANDX_SHARED_LOCK;
287
288         LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
289         LOffset.LowPart  = CM_FLSHARE_OFFSET_LOW;
290         LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
291         LLength.LowPart  = CM_FLSHARE_LENGTH_LOW;
292
293         code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
294
295         if (code == 0) {
296             cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
297         } else {
298             /* In this case, we allow the file open to go through even
299                though we can't enforce mandatory locking on the
300                file. */
301             if (code == CM_ERROR_NOACCESS &&
302                 !(rights & PRSFS_WRITE))
303                 code = 0;
304             else {
305                 switch (code) {
306                 case CM_ERROR_ALLOFFLINE:
307                 case CM_ERROR_ALLDOWN:
308                 case CM_ERROR_ALLBUSY:
309                 case CM_ERROR_TIMEDOUT:
310                 case CM_ERROR_RETRY:
311                 case CM_ERROR_WOULDBLOCK:
312                     break;
313                 default:
314                     code = CM_ERROR_SHARING_VIOLATION;
315                 }
316             }
317         }
318     }
319
320     lock_ReleaseMutex(&scp->mx);
321
322     return code;
323 }
324
325 /* return success if we can open this file in this mode */
326 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
327                     unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp)
328 {
329     long rights;
330     long code;
331
332     /* Always allow delete; the RPC will tell us if it's OK */
333     if (desiredAccess == DELETE)
334         return 0;
335
336     rights = 0;
337
338     if (desiredAccess & AFS_ACCESS_READ)
339         rights |= PRSFS_READ;
340
341     if ((desiredAccess & AFS_ACCESS_WRITE)
342          || createDisp == 4)
343         rights |= PRSFS_WRITE;
344
345     lock_ObtainMutex(&scp->mx);
346
347     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
348                       CM_SCACHESYNC_GETSTATUS
349                       | CM_SCACHESYNC_NEEDCALLBACK);
350
351     /*
352      * If the open will fail because the volume is readonly, then we will
353      * return an access denied error instead.  This is to help brain-dead
354      * apps run correctly on replicated volumes.
355      * See defect 10007 for more information.
356      */
357     if (code == CM_ERROR_READONLY)
358         code = CM_ERROR_NOACCESS;
359     else if (code == 0 &&
360              ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
361              scp->fileType == CM_SCACHETYPE_FILE) {
362         cm_key_t key;
363         unsigned int sLockType;
364         LARGE_INTEGER LOffset, LLength;
365
366         /* Check if there's some sort of lock on the file at the
367            moment. */
368
369         key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
370         if (rights & PRSFS_WRITE)
371             sLockType = 0;
372         else
373             sLockType = LOCKING_ANDX_SHARED_LOCK;
374
375         /* single byte lock at offset 0x0100 0000 0000 0000 */
376         LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
377         LOffset.LowPart  = CM_FLSHARE_OFFSET_LOW;
378         LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
379         LLength.LowPart  = CM_FLSHARE_LENGTH_LOW;
380
381         code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
382
383         if (code == 0) {
384             cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
385         } else {
386             /* In this case, we allow the file open to go through even
387                though we can't enforce mandatory locking on the
388                file. */
389             if (code == CM_ERROR_NOACCESS &&
390                 !(rights & PRSFS_WRITE))
391                 code = 0;
392             else {
393                 switch (code) {
394                 case CM_ERROR_ALLOFFLINE:
395                 case CM_ERROR_ALLDOWN:
396                 case CM_ERROR_ALLBUSY:
397                 case CM_ERROR_TIMEDOUT:
398                 case CM_ERROR_RETRY:
399                 case CM_ERROR_WOULDBLOCK:
400                     break;
401                 default:
402                     code = CM_ERROR_SHARING_VIOLATION;
403                 }
404             }
405         }
406     }
407
408     lock_ReleaseMutex(&scp->mx);
409
410     return code;
411 }
412
413 /*
414  * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
415  * done in three steps:
416  * (1) open for deletion (NT_CREATE_AND_X)
417  * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
418  * (3) close (CLOSE)
419  * We must not do the RPC until step 3.  But if we are going to return an error
420  * code (e.g. directory not empty), we must return it by step 2, otherwise most
421  * clients will not notice it.  So we do a preliminary check.  For deleting
422  * files, this is almost free, since we have already done the RPC to get the
423  * parent directory's status bits.  But for deleting directories, we must do an
424  * additional RPC to get the directory's data to check if it is empty.  Sigh.
425  */
426 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
427         cm_req_t *reqp)
428 {
429     long code;
430     osi_hyper_t thyper;
431     cm_buf_t *bufferp;
432     cm_dirEntry_t *dep;
433     unsigned short *hashTable;
434     unsigned int i, idx;
435     int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
436
437     /* First check permissions */
438     lock_ObtainMutex(&dscp->mx);
439     code = cm_SyncOp(dscp, NULL, userp, reqp, PRSFS_DELETE,
440                       CM_SCACHESYNC_GETSTATUS
441                       | CM_SCACHESYNC_NEEDCALLBACK);
442     lock_ReleaseMutex(&dscp->mx);
443     if (code)
444         return code;
445
446     /* If deleting directory, must be empty */
447
448     if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
449         return code;
450
451     thyper.HighPart = 0; thyper.LowPart = 0;
452     lock_ObtainRead(&scp->bufCreateLock);
453     code = buf_Get(scp, &thyper, &bufferp);
454     lock_ReleaseRead(&scp->bufCreateLock);
455     if (code)
456         return code;
457
458     lock_ObtainMutex(&bufferp->mx);
459     lock_ObtainMutex(&scp->mx);
460     while (1) {
461         code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
462                           CM_SCACHESYNC_NEEDCALLBACK
463                           | CM_SCACHESYNC_READ
464                           | CM_SCACHESYNC_BUFLOCKED);
465         if (code)
466             break;
467
468         if (cm_HaveBuffer(scp, bufferp, 1))
469             break;
470
471         /* otherwise, load the buffer and try again */
472         lock_ReleaseMutex(&bufferp->mx);
473         code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
474         lock_ReleaseMutex(&scp->mx);
475         lock_ObtainMutex(&bufferp->mx);
476         lock_ObtainMutex(&scp->mx);
477         if (code)
478             break;
479     }
480
481     /* We try to determine emptiness without looking beyond the first page,
482      * and without assuming "." and ".." are present and are on the first
483      * page (though these assumptions might, after all, be reasonable).
484      */
485     hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
486     for (i=0; i<128; i++) {
487         idx = ntohs(hashTable[i]);
488         while (idx) {
489             if (idx >= 64) {
490                 BeyondPage = 1;
491                 break;
492             }
493             dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
494             if (strcmp(dep->name, ".") == 0)
495                 HaveDot = 1;
496             else if (strcmp(dep->name, "..") == 0)
497                 HaveDotDot = 1;
498             else {
499                 code = CM_ERROR_NOTEMPTY;
500                 goto done;
501             }
502             idx = ntohs(dep->next);
503         }
504     }
505     if (BeyondPage && HaveDot && HaveDotDot)
506         code = CM_ERROR_NOTEMPTY;
507     else
508         code = 0;
509   done:   
510     lock_ReleaseMutex(&bufferp->mx);
511     buf_Release(bufferp);
512     lock_ReleaseMutex(&scp->mx);
513     return code;
514 }       
515
516 /*
517  * Iterate through all entries in a directory.
518  * When the function funcp is called, the buffer is locked but the
519  * directory vnode is not.
520  *
521  * If the retscp parameter is not NULL, the parmp must be a 
522  * cm_lookupSearch_t object.  
523  */
524 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
525                   osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
526                   cm_scache_t **retscp)
527 {
528     char *tp;
529     long code;
530     cm_dirEntry_t *dep;
531     cm_buf_t *bufferp;
532     long temp;
533     osi_hyper_t dirLength;
534     osi_hyper_t bufferOffset;
535     osi_hyper_t curOffset;
536     osi_hyper_t thyper;
537     long entryInDir;
538     long entryInBuffer;
539     cm_pageHeader_t *pageHeaderp;
540     int slotInPage;
541     long nextEntryCookie;
542     int numDirChunks;   /* # of 32 byte dir chunks in this entry */
543         
544     /* get the directory size */
545     lock_ObtainMutex(&scp->mx);
546     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
547                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
548     if (code) {
549         lock_ReleaseMutex(&scp->mx);
550         return code;
551     }
552         
553     if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
554         lock_ReleaseMutex(&scp->mx);
555         return CM_ERROR_NOTDIR;
556     }   
557
558     if (retscp)                         /* if this is a lookup call */
559     {
560         cm_lookupSearch_t*      sp = parmp;
561
562 #ifdef AFS_FREELANCE_CLIENT
563         /* Freelance entries never end up in the DNLC because they
564          * do not have an associated cm_server_t
565          */
566     if ( !(cm_freelanceEnabled &&
567             sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
568             sp->fid.volume==AFS_FAKE_ROOT_VOL_ID ) )
569 #endif /* AFS_FREELANCE_CLIENT */
570     {
571         int casefold = sp->caseFold;
572         sp->caseFold = 0; /* we have a strong preference for exact matches */
573         if ( *retscp = cm_dnlcLookup(scp, sp))  /* dnlc hit */
574         {
575             sp->caseFold = casefold;
576             lock_ReleaseMutex(&scp->mx);
577             return 0;
578         }
579         sp->caseFold = casefold;
580     }
581     }   
582
583     /*
584      * XXX We only get the length once.  It might change when we drop the
585      * lock.
586      */
587     dirLength = scp->length;
588
589     lock_ReleaseMutex(&scp->mx);
590
591     bufferp = NULL;
592     bufferOffset.LowPart = bufferOffset.HighPart = 0;
593     if (startOffsetp)
594         curOffset = *startOffsetp;
595     else {
596         curOffset.HighPart = 0;
597         curOffset.LowPart = 0;
598     }   
599
600     while (1) {
601         /* make sure that curOffset.LowPart doesn't point to the first
602          * 32 bytes in the 2nd through last dir page, and that it
603          * doesn't point at the first 13 32-byte chunks in the first
604          * dir page, since those are dir and page headers, and don't
605          * contain useful information.
606          */
607         temp = curOffset.LowPart & (2048-1);
608         if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
609             /* we're in the first page */
610             if (temp < 13*32) temp = 13*32;
611         }
612         else {
613             /* we're in a later dir page */
614             if (temp < 32) temp = 32;
615         }       
616                 
617         /* make sure the low order 5 bits are zero */
618         temp &= ~(32-1);
619                 
620         /* now put temp bits back ito curOffset.LowPart */
621         curOffset.LowPart &= ~(2048-1);
622         curOffset.LowPart |= temp;
623
624         /* check if we've passed the dir's EOF */
625         if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
626             break;
627                 
628         /* see if we can use the bufferp we have now; compute in which
629          * page the current offset would be, and check whether that's
630          * the offset of the buffer we have.  If not, get the buffer.
631          */
632         thyper.HighPart = curOffset.HighPart;
633         thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
634         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
635             /* wrong buffer */
636             if (bufferp) {
637                 lock_ReleaseMutex(&bufferp->mx);
638                 buf_Release(bufferp);
639                 bufferp = NULL;
640             }
641
642             lock_ObtainRead(&scp->bufCreateLock);
643             code = buf_Get(scp, &thyper, &bufferp);
644             lock_ReleaseRead(&scp->bufCreateLock);
645             if (code) {
646                 /* if buf_Get() fails we do not have a buffer object to lock */
647                 bufferp = NULL;
648                 break;
649             }
650
651 #ifdef AFSIFS
652                         /* for the IFS version, we bulkstat the dirents because this
653                            routine is used in place of smb_ReceiveCoreSearchDir.  our
654                            other option is to modify smb_ReceiveCoreSearchDir itself, 
655                            but this seems to be the proper use for cm_ApplyDir. */
656             lock_ObtainMutex(&scp->mx);
657             if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
658                  && (scp->bulkStatProgress.QuadPart <= thyper.QuadPart))
659             {
660                 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
661                 cm_TryBulkStat(scp, &thyper, userp, reqp);
662                 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
663                 scp->bulkStatProgress = thyper;
664             }
665             lock_ReleaseMutex(&scp->mx);
666 #endif
667
668             lock_ObtainMutex(&bufferp->mx);
669             bufferOffset = thyper;
670
671             /* now get the data in the cache */
672             while (1) {
673                 lock_ObtainMutex(&scp->mx);
674                 code = cm_SyncOp(scp, bufferp, userp, reqp,
675                                   PRSFS_LOOKUP,
676                                   CM_SCACHESYNC_NEEDCALLBACK
677                                   | CM_SCACHESYNC_READ
678                                   | CM_SCACHESYNC_BUFLOCKED);
679                 if (code) {
680                     lock_ReleaseMutex(&scp->mx);
681                     break;
682                 }
683                                 
684                 if (cm_HaveBuffer(scp, bufferp, 1)) {
685                     lock_ReleaseMutex(&scp->mx);
686                     break;
687                 }
688
689                 /* otherwise, load the buffer and try again */
690                 lock_ReleaseMutex(&bufferp->mx);
691                 code = cm_GetBuffer(scp, bufferp, NULL, userp,
692                                     reqp);
693                 lock_ReleaseMutex(&scp->mx);
694                 lock_ObtainMutex(&bufferp->mx);
695                 if (code) 
696                     break;
697             }
698             if (code) {
699                 lock_ReleaseMutex(&bufferp->mx);
700                 buf_Release(bufferp);
701                 bufferp = NULL;
702                 break;
703             }
704         }       /* if (wrong buffer) ... */
705            
706         /* now we have the buffer containing the entry we're interested
707          * in; copy it out if it represents a non-deleted entry.
708          */
709         entryInDir = curOffset.LowPart & (2048-1);
710         entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
711
712         /* page header will help tell us which entries are free.  Page
713          * header can change more often than once per buffer, since
714          * AFS 3 dir page size may be less than (but not more than) a
715          * buffer package buffer.
716          */
717         /* only look intra-buffer */
718         temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
719         temp &= ~(2048 - 1);    /* turn off intra-page bits */
720         pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
721
722         /* now determine which entry we're looking at in the page.  If
723          * it is free (there's a free bitmap at the start of the dir),
724          * we should skip these 32 bytes.
725          */
726         slotInPage = (entryInDir & 0x7e0) >> 5;
727         if (!(pageHeaderp->freeBitmap[slotInPage>>3]
728                & (1 << (slotInPage & 0x7)))) {
729             /* this entry is free */
730             numDirChunks = 1;   /* only skip this guy */
731             goto nextEntry;
732         }
733
734         tp = bufferp->datap + entryInBuffer;
735         dep = (cm_dirEntry_t *) tp;     /* now points to AFS3 dir entry */
736
737         /* while we're here, compute the next entry's location, too,
738          * since we'll need it when writing out the cookie into the
739          * dir listing stream.
740          */
741         numDirChunks = cm_NameEntries(dep->name, NULL);
742                 
743         /* compute the offset of the cookie representing the next entry */
744         nextEntryCookie = curOffset.LowPart
745             + (CM_DIR_CHUNKSIZE * numDirChunks);
746
747         if (dep->fid.vnode != 0) {
748             /* this is one of the entries to use: it is not deleted */
749             code = (*funcp)(scp, dep, parmp, &curOffset);
750             if (code) 
751                 break;
752         }       /* if we're including this name */
753                 
754       nextEntry:
755         /* and adjust curOffset to be where the new cookie is */
756         thyper.HighPart = 0;
757         thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
758         curOffset = LargeIntegerAdd(thyper, curOffset);
759     }           /* while copying data for dir listing */
760
761     /* release the mutex */
762     if (bufferp) {
763         lock_ReleaseMutex(&bufferp->mx);
764         buf_Release(bufferp);
765     }
766     return code;
767 }
768
769 int cm_NoneUpper(char *s)
770 {
771     char c;
772     while (c = *s++)
773         if (c >= 'A' && c <= 'Z')
774             return 0;
775     return 1;
776 }
777
778 int cm_NoneLower(char *s)
779 {
780     char c;
781     while (c = *s++)
782         if (c >= 'a' && c <= 'z')
783             return 0;
784     return 1;
785 }
786
787 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
788                           osi_hyper_t *offp)
789 {
790     cm_lookupSearch_t *sp;
791     int match;
792     char shortName[13];
793     char *matchName;
794
795     sp = (cm_lookupSearch_t *) rockp;
796
797     matchName = dep->name;
798     if (sp->caseFold)
799         match = cm_stricmp(matchName, sp->searchNamep);
800     else
801         match = strcmp(matchName, sp->searchNamep);
802
803     if (match != 0
804          && sp->hasTilde
805          && !cm_Is8Dot3(dep->name)) {
806         matchName = shortName;
807         cm_Gen8Dot3Name(dep, shortName, NULL);
808         if (sp->caseFold)
809             match = cm_stricmp(matchName, sp->searchNamep);
810         else
811             match = strcmp(matchName, sp->searchNamep);
812     }
813
814     if (match != 0)
815         return 0;
816
817     sp->found = 1;
818     if(!sp->caseFold) 
819         sp->ExactFound = 1;
820
821     if (!sp->caseFold || matchName == shortName) {
822         sp->fid.vnode = ntohl(dep->fid.vnode);
823         sp->fid.unique = ntohl(dep->fid.unique);
824         return CM_ERROR_STOPNOW;
825     }
826
827     /*
828      * If we get here, we are doing a case-insensitive search, and we
829      * have found a match.  Now we determine what kind of match it is:
830      * exact, lower-case, upper-case, or none of the above.  This is done
831      * in order to choose among matches, if there are more than one.
832      */
833
834     /* Exact matches are the best. */
835     match = strcmp(matchName, sp->searchNamep);
836     if (match == 0) {
837         sp->ExactFound = 1;
838         sp->fid.vnode = ntohl(dep->fid.vnode);
839         sp->fid.unique = ntohl(dep->fid.unique);
840         return CM_ERROR_STOPNOW;
841     }
842
843     /* Lower-case matches are next. */
844     if (sp->LCfound)
845         return 0;
846     if (cm_NoneUpper(matchName)) {
847         sp->LCfound = 1;
848         goto inexact;
849     }
850
851     /* Upper-case matches are next. */
852     if (sp->UCfound)
853         return 0;
854     if (cm_NoneLower(matchName)) {
855         sp->UCfound = 1;
856         goto inexact;
857     }
858
859     /* General matches are last. */
860     if (sp->NCfound)
861         return 0;
862     sp->NCfound = 1;
863
864   inexact:
865     sp->fid.vnode = ntohl(dep->fid.vnode);
866     sp->fid.unique = ntohl(dep->fid.unique);
867     return 0;
868 }       
869
870 /* read the contents of a mount point into the appropriate string.
871  * called with locked scp, and returns with locked scp.
872  */
873 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
874 {
875     long code;
876     cm_buf_t *bufp;
877     osi_hyper_t thyper;
878     int tlen;
879
880     if (scp->mountPointStringp[0]) 
881         return 0;
882         
883     /* otherwise, we have to read it in */
884     lock_ReleaseMutex(&scp->mx);
885
886     lock_ObtainRead(&scp->bufCreateLock);
887     thyper.LowPart = thyper.HighPart = 0;
888     code = buf_Get(scp, &thyper, &bufp);
889     lock_ReleaseRead(&scp->bufCreateLock);
890
891     lock_ObtainMutex(&scp->mx);
892     if (code) {
893         return code;
894     }
895     while (1) {
896         code = cm_SyncOp(scp, bufp, userp, reqp, 0,
897                           CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
898         if (code) {
899             goto done;
900         }
901
902         if (cm_HaveBuffer(scp, bufp, 0)) 
903             break;
904
905         /* otherwise load buffer */
906         code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
907         if (code) {
908             goto done;
909         }
910     }
911     /* locked, has callback, has valid data in buffer */
912     if ((tlen = scp->length.LowPart) > 1000) 
913         return CM_ERROR_TOOBIG;
914     if (tlen <= 0) {
915         code = CM_ERROR_INVAL;
916         goto done;
917     }
918
919     /* someone else did the work while we were out */
920     if (scp->mountPointStringp[0]) {
921         code = 0;
922         goto done;
923     }
924
925     /* otherwise, copy out the link */
926     memcpy(scp->mountPointStringp, bufp->datap, tlen);
927
928     /* now make it null-terminated.  Note that the original contents of a
929      * link that is a mount point is "#volname." where "." is there just to
930      * be turned into a null.  That is, we can trash the last char of the
931      * link without damaging the vol name.  This is a stupid convention,
932      * but that's the protocol.
933      */
934     scp->mountPointStringp[tlen-1] = 0;
935     code = 0;
936
937   done:
938     if (bufp) 
939         buf_Release(bufp);
940     return code;
941 }
942
943 /* called with a locked scp and chases the mount point, yielding outScpp.
944  * scp remains locked, just for simplicity of describing the interface.
945  */
946 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
947                          cm_req_t *reqp, cm_scache_t **outScpp)
948 {
949     char *cellNamep;
950     char *volNamep;
951     int tlen;
952     long code;
953     char *cp;
954     char *mpNamep;
955     cm_volume_t *volp;
956     cm_cell_t *cellp;
957     char mtType;
958     cm_fid_t tfid;
959     size_t vnLength;
960     int type;
961
962     if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
963         tfid = scp->mountRootFid;
964         lock_ReleaseMutex(&scp->mx);
965         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
966         lock_ObtainMutex(&scp->mx);
967         return code;
968     }
969
970     /* parse the volume name */
971     mpNamep = scp->mountPointStringp;
972     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 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3091 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3092 #else
3093 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3094 #endif
3095
3096 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3097
3098 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3099 {
3100     afs_int64 int_begin;
3101     afs_int64 int_end;
3102
3103     int_begin = MAX(pos->offset, neg->offset);
3104     int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3105
3106     if(int_begin < int_end) {
3107         if(int_begin == pos->offset) {
3108             pos->length = pos->offset + pos->length - int_end;
3109             pos->offset = int_end;
3110         } else if(int_end == pos->offset + pos->length) {
3111             pos->length = int_begin - pos->offset;
3112         }
3113
3114         /* We only subtract ranges if the resulting range is
3115            contiguous.  If we try to support non-contigous ranges, we
3116            aren't actually improving performance. */
3117     }
3118 }
3119
3120 /* Called with scp->mx held.  Returns 0 if all is clear to read the
3121    specified range by the client identified by key.
3122  */
3123 long cm_LockCheckRead(cm_scache_t *scp, 
3124                       LARGE_INTEGER LOffset, 
3125                       LARGE_INTEGER LLength, 
3126                       cm_key_t key)
3127 {
3128 #ifndef ADVISORY_LOCKS
3129
3130     cm_file_lock_t *fileLock;
3131     osi_queue_t *q;
3132     long code = 0;
3133     cm_range_t range;
3134     int substract_ranges = FALSE;
3135
3136     range.offset = LOffset.QuadPart;
3137     range.length = LLength.QuadPart;
3138
3139     /*
3140
3141      1. for all _a_ in (Offset,+Length), all of the following is true:
3142
3143        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3144          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3145          shared.
3146
3147        1.2 For each LOST lock L in S->fileLocks such that _a_ in
3148          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3149          Key(C)
3150
3151     */
3152
3153     lock_ObtainRead(&cm_scacheLock);
3154
3155     for(q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3156         fileLock = 
3157             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3158
3159         if (INTERSECT_RANGE(range, fileLock->range)) {
3160             if(IS_LOCK_ACTIVE(fileLock)) {
3161                 if(fileLock->key == key) {
3162
3163                     /* If there is an active lock for this client, it
3164                        is safe to substract ranges.*/
3165                     cm_LockRangeSubtract(&range, &fileLock->range);
3166                     substract_ranges = TRUE;
3167                 } else {
3168                     if(fileLock->lockType != LockRead) {
3169                         code = CM_ERROR_LOCK_CONFLICT;
3170                         break;
3171                     }
3172
3173                     /* even if the entire range is locked for reading,
3174                        we still can't grant the lock at this point
3175                        because the client may have lost locks. That
3176                        is, unless we have already seen an active lock
3177                        belonging to the client, in which case there
3178                        can't be any lost locks for this client. */
3179                     if(substract_ranges)
3180                         cm_LockRangeSubtract(&range, &fileLock->range);
3181                 }
3182             } else if(IS_LOCK_LOST(fileLock) &&
3183                       (fileLock->key == key || fileLock->lockType == LockWrite)) {
3184                 code = CM_ERROR_BADFD;
3185                 break;
3186             }
3187         }
3188     }
3189
3190     lock_ReleaseRead(&cm_scacheLock);
3191
3192     osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3193               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3194
3195     return code;
3196
3197 #else
3198
3199     return 0;
3200
3201 #endif
3202 }
3203
3204 /* Called with scp->mx held.  Returns 0 if all is clear to write the
3205    specified range by the client identified by key.
3206  */
3207 long cm_LockCheckWrite(cm_scache_t *scp,
3208                        LARGE_INTEGER LOffset,
3209                        LARGE_INTEGER LLength,
3210                        cm_key_t key)
3211 {
3212 #ifndef ADVISORY_LOCKS
3213
3214     cm_file_lock_t *fileLock;
3215     osi_queue_t *q;
3216     long code = 0;
3217     cm_range_t range;
3218
3219     range.offset = LOffset.QuadPart;
3220     range.length = LLength.QuadPart;
3221
3222     /*
3223    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3224
3225    2. for all _a_ in (Offset,+Length), one of the following is true:
3226
3227        2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3228          lock L such that _a_ in (L->LOffset,+L->LLength).
3229
3230        2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3231          exclusive.
3232     */
3233
3234     lock_ObtainRead(&cm_scacheLock);
3235
3236     for(q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3237         fileLock = 
3238             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3239
3240         if(INTERSECT_RANGE(range, fileLock->range)) {
3241             if(IS_LOCK_ACTIVE(fileLock)) {
3242                 if(fileLock->key == key) {
3243                     if(fileLock->lockType == LockWrite) {
3244
3245                         /* if there is an active lock for this client, it
3246                            is safe to substract ranges */
3247                         cm_LockRangeSubtract(&range, &fileLock->range);
3248                     } else {
3249                         code = CM_ERROR_LOCK_CONFLICT;
3250                         break;
3251                     }
3252                 } else {
3253                     code = CM_ERROR_LOCK_CONFLICT;
3254                     break;
3255                 }
3256             } else if(IS_LOCK_LOST(fileLock)) {
3257                 code = CM_ERROR_BADFD;
3258                 break;
3259             }
3260         }
3261     }
3262
3263     lock_ReleaseRead(&cm_scacheLock);
3264
3265     osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3266               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3267
3268     return code;
3269
3270 #else
3271
3272     return 0;
3273
3274 #endif
3275 }
3276
3277 /* Forward dcl. */
3278 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3279
3280 /* Called with cm_scacheLock write locked */
3281 static cm_file_lock_t * cm_GetFileLock(void) {
3282     cm_file_lock_t * l;
3283
3284     l = (cm_file_lock_t *) cm_freeFileLocks;
3285     if(l) {
3286         osi_QRemove(&cm_freeFileLocks, &l->q);
3287     } else {
3288         l = malloc(sizeof(cm_file_lock_t));
3289         osi_assert(l);
3290     }
3291
3292     memset(l, 0, sizeof(cm_file_lock_t));
3293
3294     return l;
3295 }
3296
3297 /* Called with cm_scacheLock write locked */
3298 static void cm_PutFileLock(cm_file_lock_t *l) {
3299     osi_QAdd(&cm_freeFileLocks, &l->q);
3300 }
3301
3302 /* called with scp->mx held */
3303 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
3304              LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
3305              cm_key_t key,
3306              int allowWait, cm_user_t *userp, cm_req_t *reqp,
3307              cm_file_lock_t **lockpp)
3308 {
3309     long code = 0;
3310     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
3311     AFSFid tfid;
3312     AFSVolSync volSync;
3313     cm_conn_t *connp;
3314     cm_file_lock_t *fileLock;
3315     osi_queue_t *q;
3316     struct rx_connection * callp;
3317     cm_range_t range;
3318     int wait_unlock = FALSE;
3319
3320     osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
3321              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
3322     osi_Log3(afsd_logp, "... allowWait %d key 0x%x:%x", allowWait, 
3323              (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
3324
3325     /*
3326    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3327
3328    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3329       true:
3330
3331        3.1 If L->LockType is exclusive then there does NOT exist a
3332          ACCEPTED lock M in S->fileLocks such that _a_ in
3333          (M->LOffset,+M->LLength).
3334
3335        3.2 If L->LockType is shared then for each ACCEPTED lock M in
3336          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3337          M->LockType is shared.
3338
3339    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3340
3341        4.1 M->key != Key(C)
3342
3343        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3344          and (M->LOffset,+M->LLength) do not intersect.
3345     */
3346
3347     range.offset = LOffset.QuadPart;
3348     range.length = LLength.QuadPart;
3349
3350     lock_ObtainRead(&cm_scacheLock);
3351
3352     for(q = scp->fileLocksH; q; q = osi_QNext(q)) {
3353         fileLock =
3354             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3355
3356         if(IS_LOCK_LOST(fileLock)) {
3357             if (fileLock->key == key) {
3358                 code = CM_ERROR_BADFD;
3359                 break;
3360             } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
3361                 code = CM_ERROR_WOULDBLOCK;
3362                 wait_unlock = TRUE;
3363                 break;
3364             }
3365         }
3366
3367         /* we don't need to check for deleted locks here since deleted
3368            locks are dequeued from scp->fileLocks */
3369         if(IS_LOCK_ACCEPTED(fileLock) &&
3370            INTERSECT_RANGE(range, fileLock->range)) {
3371
3372             if((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
3373                 fileLock->lockType != LockRead) {
3374                 wait_unlock = TRUE;
3375                 code = CM_ERROR_WOULDBLOCK;
3376                 break;
3377             }
3378         }
3379     }
3380
3381     lock_ReleaseRead(&cm_scacheLock);
3382
3383     if(code == 0 && SERVERLOCKS_ENABLED(scp)) {
3384         if(Which == scp->serverLock ||
3385            (Which == LockRead && scp->serverLock == LockWrite)) {
3386
3387             /* we already have the lock we need */
3388             osi_Log3(afsd_logp, "   we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]", 
3389                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3390             code = 0; /* redundant */
3391
3392         } else if((scp->exclusiveLocks > 0) ||
3393                 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
3394
3395             /* We are already waiting for some other lock.  We should
3396                wait for the daemon to catch up instead of generating a
3397                flood of SetLock calls. */
3398             osi_Log3(afsd_logp, "   already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
3399                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3400             code = CM_ERROR_WOULDBLOCK;
3401
3402         } else {
3403             cm_fid_t cfid;
3404             int newLock;
3405
3406             if (scp->serverLock == LockRead && Which == LockWrite) {
3407             
3408                 /* We want to escalate the lock to a LockWrite.
3409                    Unfortunately that's not really possible without
3410                    letting go of the current lock.  But for now we do
3411                    it anyway. */
3412
3413                 osi_Log0(afsd_logp, "   attempting to UPGRADE from LockRead to LockWrite.");
3414
3415                 tfid.Volume = scp->fid.volume;
3416                 tfid.Vnode = scp->fid.vnode;
3417                 tfid.Unique = scp->fid.unique;
3418                 cfid = scp->fid;
3419
3420                 lock_ReleaseMutex(&scp->mx);
3421
3422                 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3423
3424                 do {
3425                     code = cm_Conn(&cfid, userp, reqp, &connp);
3426                     if (code) 
3427                         break;
3428
3429                     callp = cm_GetRxConn(connp);
3430                     code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3431                     rx_PutConnection(callp);
3432
3433                 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3434                                     NULL, NULL, code));
3435                 code = cm_MapRPCError(code, reqp);
3436
3437                 if (code)
3438                     osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
3439                 else
3440                     osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
3441         
3442                 lock_ObtainMutex(&scp->mx);
3443
3444                 if (code) {
3445                     /* We couldn't release the lock */
3446                     goto check_code;
3447                 } else {
3448                     scp->serverLock = -1;
3449                 }
3450             }
3451
3452             /* We need to obtain a server lock of type Which in order
3453                to assert this file lock */
3454             tfid.Volume = scp->fid.volume;
3455             tfid.Vnode = scp->fid.vnode;
3456             tfid.Unique = scp->fid.unique;
3457             cfid = scp->fid;
3458
3459 #ifndef AGGRESSIVE_LOCKS
3460             newLock = Which;
3461 #else
3462             newLock = LockWrite;
3463 #endif
3464             osi_Log3(afsd_logp, "CALL SetLock scp 0x%p from %d to %d", scp, (int) scp->serverLock, newLock);
3465
3466             lock_ReleaseMutex(&scp->mx);
3467
3468             do {
3469                 code = cm_Conn(&cfid, userp, reqp, &connp);
3470                 if (code) 
3471                     break;
3472
3473                 callp = cm_GetRxConn(connp);
3474                 code = RXAFS_SetLock(callp, &tfid, newLock,
3475                                      &volSync);
3476                 rx_PutConnection(callp);
3477
3478             } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3479                                 NULL, NULL, code));
3480
3481             code = cm_MapRPCError(code, reqp);
3482             
3483             if (code) {
3484                 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3485             } else {
3486                 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3487             }
3488
3489             if (code == CM_ERROR_WOULDBLOCK && newLock != Which) {
3490                 /* we wanted LockRead.  We tried LockWrite. Now try LockRead again */
3491                 newLock = Which;
3492
3493                 /* am I sane? */
3494                 osi_assert(newLock == LockRead);
3495                 
3496                 osi_Log3(afsd_logp, "CALL SetLock AGAIN scp 0x%p from %d to %d", 
3497                          scp, (int) scp->serverLock, newLock);
3498
3499                 do {
3500                     code = cm_Conn(&cfid, userp, reqp, &connp);
3501                     if (code) 
3502                         break;
3503
3504                     callp = cm_GetRxConn(connp);
3505                     code = RXAFS_SetLock(callp, &tfid, newLock,
3506                                          &volSync);
3507                     rx_PutConnection(callp);
3508
3509                 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3510                                     NULL, NULL, code));
3511
3512                 code = cm_MapRPCError(code, reqp);                
3513
3514                 if (code) {
3515                     osi_Log1(afsd_logp, "CALL SetLock FAILURE AGAIN, code 0x%x", code);
3516                 } else {
3517                     osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3518                 }
3519             }
3520
3521             lock_ObtainMutex(&scp->mx);
3522
3523             if(code == 0)
3524                 scp->serverLock = newLock;
3525             else {
3526                 if ((scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
3527                     scp->serverLock == -1) {
3528                     /* Oops. We lost the lock. */
3529                     cm_LockMarkSCacheLost(scp);
3530                 }
3531             }
3532         }
3533     } else if (code == 0) {     /* server locks not enabled */
3534         osi_Log0(afsd_logp,
3535                  "  Skipping server lock for scp");
3536     }
3537
3538  check_code:
3539
3540     if (code != 0) {
3541         /* Special case error translations
3542
3543            Applications don't expect certain errors from a
3544            LockFile/UnlockFile call.  We need to translate some error
3545            code to codes that apps expect and handle. */
3546
3547         /* We shouldn't actually need to handle this case since we
3548            simulate locks for RO scps anyway. */
3549         if (code == CM_ERROR_READONLY) {
3550             osi_Log0(afsd_logp, "   Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
3551             code = CM_ERROR_NOACCESS;
3552         }
3553     }
3554
3555     if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait)) {
3556
3557         lock_ObtainWrite(&cm_scacheLock);
3558         fileLock = cm_GetFileLock();
3559         lock_ReleaseWrite(&cm_scacheLock);
3560 #ifdef DEBUG
3561         fileLock->fid = scp->fid;
3562 #endif
3563         fileLock->key = key;
3564         fileLock->lockType = Which;
3565         cm_HoldUser(userp);
3566         fileLock->userp = userp;
3567         fileLock->range = range;
3568         fileLock->flags = (code == 0 ? 0 : 
3569                            ((wait_unlock)?
3570                             CM_FILELOCK_FLAG_WAITUNLOCK :
3571                             CM_FILELOCK_FLAG_WAITLOCK));
3572
3573         if (!SERVERLOCKS_ENABLED(scp))
3574             fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
3575
3576         fileLock->lastUpdate = (code == 0) ? time(NULL) : 0;
3577
3578         osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
3579
3580         lock_ObtainWrite(&cm_scacheLock);
3581         cm_HoldSCacheNoLock(scp);
3582         fileLock->scp = scp;
3583         osi_QAdd(&cm_allFileLocks, &fileLock->q);
3584         lock_ReleaseWrite(&cm_scacheLock);
3585
3586         if (code != 0) {
3587             *lockpp = fileLock;
3588         }
3589
3590         if (IS_LOCK_ACCEPTED(fileLock)) {
3591             if(Which == LockRead)
3592                 scp->sharedLocks++;
3593             else
3594                 scp->exclusiveLocks++;
3595         }
3596
3597         osi_Log2(afsd_logp, "cm_Lock Lock added 0x%p flags 0x%x", fileLock, fileLock->flags);
3598         osi_Log4(afsd_logp, "   scp[0x%p] exclusives[%d] shared[%d] serverLock[%d]",
3599                  scp, scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3600     }
3601
3602     return code;
3603 }
3604
3605 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
3606
3607 /* Called with scp->mx held */
3608 long cm_UnlockByKey(cm_scache_t * scp,
3609                     cm_key_t key,
3610                     int flags,
3611                     cm_user_t * userp,
3612                      cm_req_t * reqp)
3613 {
3614     long code = 0;
3615     AFSFid tfid;
3616     AFSVolSync volSync;
3617     cm_conn_t *connp;
3618     cm_file_lock_t *fileLock;
3619     osi_queue_t *q, *qn;
3620     struct rx_connection * callp;
3621     int n_unlocks = 0;
3622
3623     osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key 0x%x:%x flags=0x%x",
3624              scp,
3625              (unsigned long)(key >> 32),
3626              (unsigned long)(key & 0xffffffff),
3627              flags);
3628
3629     lock_ObtainWrite(&cm_scacheLock);
3630
3631     for(q = scp->fileLocksH; q; q = qn) {
3632         qn = osi_QNext(q);
3633
3634         fileLock = (cm_file_lock_t *)
3635             ((char *) q - offsetof(cm_file_lock_t, fileq));
3636
3637 #ifdef DEBUG
3638         osi_Log4(afsd_logp, "   Checking lock[0x%x] range[%d,+%d] type[%d]",
3639                  fileLock,
3640                  (unsigned long) fileLock->range.offset,
3641                  (unsigned long) fileLock->range.length,
3642                 fileLock->lockType);
3643         osi_Log3(afsd_logp, "     key[0x%x:%x] flags[0x%x]",
3644                  (unsigned long)(fileLock->key >> 32),
3645                  (unsigned long)(fileLock->key & 0xffffffff),
3646                  fileLock->flags);
3647
3648         if(cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
3649             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
3650             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3651                      fileLock->fid.cell,
3652                      fileLock->fid.volume,
3653                      fileLock->fid.vnode,
3654                      fileLock->fid.unique);
3655             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3656                      fileLock->scp->fid.cell,
3657                      fileLock->scp->fid.volume,
3658                      fileLock->scp->fid.vnode,
3659                      fileLock->scp->fid.unique);
3660             osi_assert(FALSE);
3661         }
3662 #endif
3663
3664         if (!IS_LOCK_DELETED(fileLock) &&
3665             cm_KeyEquals(fileLock->key, key, flags)) {
3666             osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
3667                     fileLock->range.offset,
3668                     fileLock->range.length,
3669                     fileLock->lockType);
3670
3671             if (scp->fileLocksT == q)
3672                 scp->fileLocksT = osi_QPrev(q);
3673             osi_QRemove(&scp->fileLocksH,q);
3674
3675             if(IS_LOCK_ACCEPTED(fileLock)) {
3676                 if(fileLock->lockType == LockRead)
3677                     scp->sharedLocks--;
3678                 else
3679                     scp->exclusiveLocks--;
3680             }
3681
3682             fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
3683
3684             cm_ReleaseUser(fileLock->userp);
3685             cm_ReleaseSCacheNoLock(scp);
3686
3687             fileLock->userp = NULL;
3688             fileLock->scp = NULL;
3689
3690             n_unlocks++;
3691         }
3692     }
3693
3694     lock_ReleaseWrite(&cm_scacheLock);
3695
3696     if(n_unlocks == 0) {
3697         osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
3698         osi_Log3(afsd_logp, "   Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
3699                  scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3700         
3701         return 0;
3702     }
3703
3704     osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
3705
3706     osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
3707     osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
3708
3709     if (!SERVERLOCKS_ENABLED(scp)) {
3710         osi_Log0(afsd_logp, "  Skipping server lock for scp");
3711         goto done;
3712     }
3713
3714     /* Ideally we would go through the rest of the locks to determine
3715      * if one or more locks that were formerly in WAITUNLOCK can now
3716      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
3717      * scp->sharedLocks accordingly.  However, the retrying of locks
3718      * in that manner is done cm_RetryLock() manually.
3719      */
3720
3721     if (scp->serverLock == LockWrite && scp->exclusiveLocks == 0 && scp->sharedLocks > 0) {
3722
3723         cm_fid_t cfid;
3724
3725         /* The serverLock should be downgraded to LockRead */
3726         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
3727
3728         tfid.Volume = scp->fid.volume;
3729         tfid.Vnode = scp->fid.vnode;
3730         tfid.Unique = scp->fid.unique;
3731         cfid = scp->fid;
3732
3733         lock_ReleaseMutex(&scp->mx);
3734
3735         osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3736
3737         do {
3738             code = cm_Conn(&cfid, userp, reqp, &connp);
3739             if (code) 
3740                 break;
3741
3742             callp = cm_GetRxConn(connp);
3743             code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3744             rx_PutConnection(callp);
3745             
3746         } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3747                             NULL, NULL, code));
3748         code = cm_MapRPCError(code, reqp);
3749
3750         if (code)
3751             osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
3752         else
3753             osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
3754         
3755         lock_ObtainMutex(&scp->mx);
3756
3757         if (code) {
3758             /* so we couldn't release it.  Just let the lock be for now */
3759             code = 0;
3760             goto done;
3761         } else {
3762             scp->serverLock = -1;
3763         }
3764
3765         tfid.Volume = scp->fid.volume;
3766         tfid.Vnode = scp->fid.vnode;
3767         tfid.Unique = scp->fid.unique;
3768         cfid = scp->fid;
3769
3770         osi_Log3(afsd_logp, "CALL SetLock scp 0x%p from %d to %d", scp, (int) scp->serverLock, LockRead);
3771
3772         lock_ReleaseMutex(&scp->mx);
3773
3774         do {
3775
3776             code = cm_Conn(&cfid, userp, reqp, &connp);
3777             if (code) 
3778                 break;
3779
3780             callp = cm_GetRxConn(connp);
3781             code = RXAFS_SetLock(callp, &tfid, LockRead,
3782                                  &volSync);
3783
3784             rx_PutConnection(callp);
3785             
3786         } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3787                             NULL, NULL, code));
3788
3789         if (code)
3790             osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3791         else {
3792             osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3793         }
3794
3795         lock_ObtainMutex(&scp->mx);
3796         
3797         if(code == 0)
3798             scp->serverLock = LockRead;
3799         else {
3800             if ((scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
3801                 (scp->serverLock == -1)) {
3802                 /* Oopsie */
3803                 cm_LockMarkSCacheLost(scp);
3804             }
3805         }
3806
3807         /* failure here has no bearing on the return value of
3808            cm_Unlock() */
3809         code = 0;
3810
3811     } else if(scp->serverLock != (-1) && scp->exclusiveLocks == 0 && scp->sharedLocks == 0) {
3812         cm_fid_t cfid;
3813
3814         /* The serverLock should be released entirely */
3815
3816         tfid.Volume = scp->fid.volume;
3817         tfid.Vnode = scp->fid.vnode;
3818         tfid.Unique = scp->fid.unique;
3819         cfid = scp->fid;
3820
3821         lock_ReleaseMutex(&scp->mx);
3822
3823         osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3824
3825         do {
3826             code = cm_Conn(&cfid, userp, reqp, &connp);
3827             if (code) 
3828                 break;
3829
3830             callp = cm_GetRxConn(connp);
3831             code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3832             rx_PutConnection(callp);
3833
3834         } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3835                             NULL, NULL, code));
3836         code = cm_MapRPCError(code, reqp);
3837
3838         if (code)
3839             osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
3840         else
3841             osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
3842         
3843         lock_ObtainMutex(&scp->mx);
3844
3845         if (code == 0)
3846             scp->serverLock = (-1);
3847     }
3848
3849  done:
3850
3851     osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
3852     osi_Log3(afsd_logp, "   Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
3853              scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3854
3855     return code;
3856 }
3857
3858 long cm_Unlock(cm_scache_t *scp, 
3859                unsigned char sLockType,
3860                LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
3861                cm_key_t key, 
3862                cm_user_t *userp, 
3863                cm_req_t *reqp)
3864 {
3865     long code = 0;
3866     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
3867     AFSFid tfid;
3868     AFSVolSync volSync;
3869     cm_conn_t *connp;
3870     cm_file_lock_t *fileLock;
3871     osi_queue_t *q;
3872     int release_userp = FALSE;
3873     struct rx_connection * callp;
3874
3875     osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
3876              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
3877     osi_Log2(afsd_logp, "... key 0x%x:%x",
3878              (unsigned long) (key >> 32), (unsigned long) (key & 0xffffffff));
3879
3880     lock_ObtainRead(&cm_scacheLock);
3881
3882     for(q = scp->fileLocksH; q; q = osi_QNext(q)) {
3883         fileLock = (cm_file_lock_t *)
3884             ((char *) q - offsetof(cm_file_lock_t, fileq));
3885
3886 #ifdef DEBUG
3887         if(cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
3888             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
3889             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3890                      fileLock->fid.cell,
3891                      fileLock->fid.volume,
3892                      fileLock->fid.vnode,
3893                      fileLock->fid.unique);
3894             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3895                      fileLock->scp->fid.cell,
3896                      fileLock->scp->fid.volume,
3897                      fileLock->scp->fid.vnode,
3898                      fileLock->scp->fid.unique);
3899             osi_assert(FALSE);
3900         }
3901 #endif
3902         if (!IS_LOCK_DELETED(fileLock) &&
3903             fileLock->key == key &&
3904             fileLock->range.offset == LOffset.QuadPart &&
3905             fileLock->range.length == LLength.QuadPart) {
3906             break;
3907         }
3908     }
3909
3910     if(!q) {
3911         osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
3912         
3913         lock_ReleaseRead(&cm_scacheLock);
3914
3915         /* The lock didn't exist anyway. *shrug* */
3916         return 0;
3917     }
3918
3919     /* discard lock record */
3920     if (scp->fileLocksT == q)
3921         scp->fileLocksT = osi_QPrev(q);
3922     osi_QRemove(&scp->fileLocksH, q);
3923
3924     lock_ReleaseRead(&cm_scacheLock);
3925
3926     /*
3927      * Don't delete it here; let the daemon delete it, to simplify
3928      * the daemon's traversal of the list.
3929      */
3930
3931     lock_ObtainWrite(&cm_scacheLock);
3932
3933     if(IS_LOCK_ACCEPTED(fileLock)) {
3934         if(fileLock->lockType == LockRead)
3935             scp->sharedLocks--;
3936         else
3937             scp->exclusiveLocks--;
3938     }
3939
3940     fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
3941     if (userp != NULL) {
3942         cm_ReleaseUser(fileLock->userp);
3943     } else {
3944         userp = fileLock->userp;
3945         release_userp = TRUE;
3946     }
3947     fileLock->userp = NULL;
3948     cm_ReleaseSCacheNoLock(scp);
3949     fileLock->scp = NULL;
3950     lock_ReleaseWrite(&cm_scacheLock);
3951
3952     if (!SERVERLOCKS_ENABLED(scp)) {
3953         osi_Log0(afsd_logp, "   Skipping server locks for scp");
3954         goto done;
3955     }
3956
3957     /* Ideally we would go through the rest of the locks to determine
3958      * if one or more locks that were formerly in WAITUNLOCK can now
3959      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
3960      * scp->sharedLocks accordingly.  However, the retrying of locks
3961      * in that manner is done cm_RetryLock() manually.
3962      */
3963
3964     if (scp->serverLock == LockWrite && scp->exclusiveLocks == 0 && scp->sharedLocks > 0) {
3965
3966         cm_fid_t cfid;
3967
3968         /* The serverLock should be downgraded to LockRead */
3969         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
3970
3971         tfid.Volume = scp->fid.volume;
3972         tfid.Vnode = scp->fid.vnode;
3973         tfid.Unique = scp->fid.unique;
3974         cfid = scp->fid;
3975
3976         lock_ReleaseMutex(&scp->mx);
3977
3978         osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3979
3980         do {
3981             code = cm_Conn(&cfid, userp, reqp, &connp);
3982             if (code) 
3983                 break;
3984
3985             callp = cm_GetRxConn(connp);
3986             code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3987             rx_PutConnection(callp);
3988             
3989         } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3990                             NULL, NULL, code));
3991
3992         code = cm_MapRPCError(code, reqp);
3993
3994         if (code)
3995             osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
3996         else
3997             osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
3998         
3999         lock_ObtainMutex(&scp->mx);
4000
4001         if (code) {
4002             /* so we couldn't release it.  Just let the lock be for now */
4003             code = 0;
4004             goto done;
4005         } else {
4006             scp->serverLock = -1;
4007         }
4008
4009         tfid.Volume = scp->fid.volume;
4010         tfid.Vnode = scp->fid.vnode;
4011         tfid.Unique = scp->fid.unique;
4012         cfid = scp->fid;
4013
4014         osi_Log3(afsd_logp, "CALL SetLock scp 0x%p from %d to %d", scp, (int) scp->serverLock, LockRead);
4015
4016         lock_ReleaseMutex(&scp->mx);
4017
4018         do {
4019
4020             code = cm_Conn(&cfid, userp, reqp, &connp);
4021             if (code) 
4022                 break;
4023
4024             callp = cm_GetRxConn(connp);
4025             code = RXAFS_SetLock(callp, &tfid, LockRead,
4026                                  &volSync);
4027
4028             rx_PutConnection(callp);
4029             
4030         } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4031                             NULL, NULL, code));
4032
4033         if (code)
4034             osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4035         else {
4036             osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4037         }
4038
4039         lock_ObtainMutex(&scp->mx);
4040         
4041         if(code == 0)
4042             scp->serverLock = LockRead;
4043         else {
4044             if ((scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4045                 (scp->serverLock == -1)) {
4046                 /* Oopsie */
4047                 cm_LockMarkSCacheLost(scp);
4048             }
4049         }
4050
4051         /* failure here has no bearing on the return value of
4052            cm_Unlock() */
4053         code = 0;
4054
4055     } else if(scp->serverLock != (-1) && scp->exclusiveLocks == 0 && scp->sharedLocks == 0) {
4056         cm_fid_t cfid;
4057
4058         /* The serverLock should be released entirely */
4059
4060         tfid.Volume = scp->fid.volume;
4061         tfid.Vnode = scp->fid.vnode;
4062         tfid.Unique = scp->fid.unique;
4063         cfid = scp->fid;
4064
4065         lock_ReleaseMutex(&scp->mx);
4066