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