windows-dbg-refcount-20061016
[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 #ifdef DEBUG_REFCOUNT
1260 long cm_LookupDbg(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1261                cm_req_t *reqp, cm_scache_t **outpScpp, char * file, long line)
1262 #else
1263 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1264                cm_req_t *reqp, cm_scache_t **outpScpp)
1265 #endif
1266 {
1267     long code;
1268     char tname[256];
1269     int sysNameIndex = 0;
1270     cm_scache_t *scp = NULL;
1271
1272 #ifdef DEBUG_REFCOUNT
1273     afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1274     osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1275 #endif
1276
1277     if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1278         if (flags & CM_FLAG_CHECKPATH)
1279             return CM_ERROR_NOSUCHPATH;
1280         else
1281             return CM_ERROR_NOSUCHFILE;
1282     }
1283
1284     for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1285         code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1286         if (code > 0) {
1287             code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1288 #ifdef DEBUG_REFCOUNT
1289             afsi_log("%s:%d cm_LookupInternal (1) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1290             osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1291 #endif
1292
1293             if (code == 0) {
1294                 *outpScpp = scp;
1295                 return 0;
1296             }
1297             if (scp) {
1298                 cm_ReleaseSCache(scp);
1299                 scp = NULL;
1300             }
1301         } else {
1302             code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1303 #ifdef DEBUG_REFCOUNT
1304             afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1305             osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1306 #endif
1307             *outpScpp = scp;
1308             return code;
1309         }
1310     }
1311
1312     /* None of the possible sysName expansions could be found */
1313     if (flags & CM_FLAG_CHECKPATH)
1314         return CM_ERROR_NOSUCHPATH;
1315     else
1316         return CM_ERROR_NOSUCHFILE;
1317 }
1318
1319 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1320 {
1321     long code;
1322     cm_conn_t *connp;
1323     AFSFid afsFid;
1324     int sflags;
1325     AFSFetchStatus newDirStatus;
1326     AFSVolSync volSync;
1327     struct rx_connection * callp;
1328
1329 #ifdef AFS_FREELANCE_CLIENT
1330     if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1331         /* deleting a mount point from the root dir. */
1332         code = cm_FreelanceRemoveMount(namep);
1333         return code;
1334     }
1335 #endif  
1336
1337     /* make sure we don't screw up the dir status during the merge */
1338     lock_ObtainMutex(&dscp->mx);
1339     sflags = CM_SCACHESYNC_STOREDATA;
1340     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1341     lock_ReleaseMutex(&dscp->mx);
1342     if (code) 
1343         return code;
1344
1345     /* make the RPC */
1346     afsFid.Volume = dscp->fid.volume;
1347     afsFid.Vnode = dscp->fid.vnode;
1348     afsFid.Unique = dscp->fid.unique;
1349
1350     osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1351     do {
1352         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1353         if (code) 
1354             continue;
1355
1356         callp = cm_GetRxConn(connp);
1357         code = RXAFS_RemoveFile(callp, &afsFid, namep,
1358                                  &newDirStatus, &volSync);
1359         rx_PutConnection(callp);
1360
1361     } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1362     code = cm_MapRPCError(code, reqp);
1363
1364     if (code)
1365         osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1366     else
1367         osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1368
1369     lock_ObtainMutex(&dscp->mx);
1370     cm_dnlcRemove(dscp, namep);
1371     cm_SyncOpDone(dscp, NULL, sflags);
1372     if (code == 0) 
1373         cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1374         else if (code == CM_ERROR_NOSUCHFILE) {
1375                 /* windows would not have allowed the request to delete the file 
1376                  * if it did not believe the file existed.  therefore, we must 
1377                  * have an inconsistent view of the world.
1378                  */
1379                 dscp->cbServerp = NULL;
1380         }
1381     lock_ReleaseMutex(&dscp->mx);
1382
1383     return code;
1384 }
1385
1386 /* called with a locked vnode, and fills in the link info.
1387  * returns this the vnode still locked.
1388  */
1389 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1390 {
1391     long code;
1392     cm_buf_t *bufp;
1393     long temp;
1394     osi_hyper_t thyper;
1395
1396     lock_AssertMutex(&linkScp->mx);
1397     if (!linkScp->mountPointStringp[0]) {
1398         /* read the link data */
1399         lock_ReleaseMutex(&linkScp->mx);
1400         thyper.LowPart = thyper.HighPart = 0;
1401         code = buf_Get(linkScp, &thyper, &bufp);
1402         lock_ObtainMutex(&linkScp->mx);
1403         if (code) 
1404             return code;
1405         while (1) {
1406             code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1407                               CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1408             if (code) {
1409                 buf_Release(bufp);
1410                 return code;
1411             }
1412             cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1413
1414             if (cm_HaveBuffer(linkScp, bufp, 0)) 
1415                 break;
1416
1417             code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1418             if (code) {
1419                 buf_Release(bufp);
1420                 return code;
1421             }
1422         } /* while loop to get the data */
1423                 
1424         /* now if we still have no link read in,
1425          * copy the data from the buffer */
1426         if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1427             buf_Release(bufp);
1428             return CM_ERROR_TOOBIG;
1429         }
1430
1431         /* otherwise, it fits; make sure it is still null (could have
1432          * lost race with someone else referencing this link above),
1433          * and if so, copy in the data.
1434          */
1435         if (!linkScp->mountPointStringp[0]) {
1436             strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1437             linkScp->mountPointStringp[temp] = 0;       /* null terminate */
1438         }
1439         buf_Release(bufp);
1440     }   /* don't have sym link contents cached */
1441
1442     return 0;
1443 }       
1444
1445 /* called with a held vnode and a path suffix, with the held vnode being a
1446  * symbolic link.  Our goal is to generate a new path to interpret, and return
1447  * this new path in newSpaceBufferp.  If the new vnode is relative to a dir
1448  * other than the directory containing the symbolic link, then the new root is
1449  * returned in *newRootScpp, otherwise a null is returned there.
1450  */
1451 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1452                       cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1453                       cm_user_t *userp, cm_req_t *reqp)
1454 {
1455     long code = 0;
1456     long len;
1457     char *linkp;
1458     cm_space_t *tsp;
1459
1460     lock_ObtainMutex(&linkScp->mx);
1461     code = cm_HandleLink(linkScp, userp, reqp);
1462     if (code) 
1463         goto done;
1464
1465     /* if we may overflow the buffer, bail out; buffer is signficantly
1466      * bigger than max path length, so we don't really have to worry about
1467      * being a little conservative here.
1468      */
1469     if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1470          >= CM_UTILS_SPACESIZE)
1471         return CM_ERROR_TOOBIG;
1472
1473     tsp = cm_GetSpace();
1474     linkp = linkScp->mountPointStringp;
1475     if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1476         if (strlen(linkp) > cm_mountRootLen)
1477             strcpy(tsp->data, linkp+cm_mountRootLen+1);
1478         else
1479             tsp->data[0] = 0;
1480         *newRootScpp = cm_data.rootSCachep;
1481         cm_HoldSCache(cm_data.rootSCachep);
1482     } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1483         if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName)))) 
1484         {
1485             char * p = &linkp[len + 3];
1486             if (strnicmp(p, "all", 3) == 0)
1487                 p += 4;
1488
1489             strcpy(tsp->data, p);
1490             for (p = tsp->data; *p; p++) {
1491                 if (*p == '\\')
1492                     *p = '/';
1493             }
1494             *newRootScpp = cm_data.rootSCachep;
1495             cm_HoldSCache(cm_data.rootSCachep);
1496         } else {
1497             linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1498             strcpy(tsp->data, linkp);
1499             *newRootScpp = NULL;
1500             code = CM_ERROR_PATH_NOT_COVERED;
1501         }
1502     } else if ( !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1503         linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1504         strcpy(tsp->data, linkp);
1505         *newRootScpp = NULL;
1506         code = CM_ERROR_PATH_NOT_COVERED;
1507     } else if (*linkp == '\\' || *linkp == '/') {
1508 #if 0   
1509         /* formerly, this was considered to be from the AFS root,
1510          * but this seems to create problems.  instead, we will just
1511          * reject the link */
1512         strcpy(tsp->data, linkp+1);
1513         *newRootScpp = cm_data.rootSCachep;
1514         cm_HoldSCache(cm_data.rootSCachep);
1515 #else
1516         /* we still copy the link data into the response so that 
1517          * the user can see what the link points to
1518          */
1519         linkScp->fileType = CM_SCACHETYPE_INVALID;
1520         strcpy(tsp->data, linkp);
1521         *newRootScpp = NULL;
1522         code = CM_ERROR_NOSUCHPATH;
1523 #endif  
1524     } else {
1525         /* a relative link */
1526         strcpy(tsp->data, linkp);
1527         *newRootScpp = NULL;
1528     }
1529     if (pathSuffixp[0] != 0) {  /* if suffix string is non-null */
1530         strcat(tsp->data, "\\");
1531         strcat(tsp->data, pathSuffixp);
1532     }
1533     *newSpaceBufferp = tsp;
1534
1535   done:
1536     lock_ReleaseMutex(&linkScp->mx);
1537     return code;
1538 }
1539 #ifdef DEBUG_REFCOUNT
1540 long cm_NameIDbg(cm_scache_t *rootSCachep, char *pathp, long flags,
1541                cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp, 
1542                char * file, long line)
1543 #else
1544 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1545                cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1546 #endif
1547 {
1548     long code;
1549     char *tp;                   /* ptr moving through input buffer */
1550     char tc;                    /* temp char */
1551     int haveComponent;          /* has new component started? */
1552     char component[256];        /* this is the new component */
1553     char *cp;                   /* component name being assembled */
1554     cm_scache_t *tscp;          /* current location in the hierarchy */
1555     cm_scache_t *nscp;          /* next dude down */
1556     cm_scache_t *dirScp;        /* last dir we searched */
1557     cm_scache_t *linkScp;       /* new root for the symlink we just
1558     * looked up */
1559     cm_space_t *psp;            /* space for current path, if we've hit
1560     * any symlinks */
1561     cm_space_t *tempsp;         /* temp vbl */
1562     char *restp;                /* rest of the pathname to interpret */
1563     int symlinkCount;           /* count of # of symlinks traversed */
1564     int extraFlag;              /* avoid chasing mt pts for dir cmd */
1565     int phase = 1;              /* 1 = tidPathp, 2 = pathp */
1566
1567 #ifdef DEBUG_REFCOUNT
1568     afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1569     osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %s tidpath %s flags 0x%x",
1570               rootSCachep, pathp ? pathp : "<NULL>", tidPathp ? tidPathp : "<NULL>", 
1571               flags);
1572 #endif
1573
1574     tp = tidPathp;
1575     if (tp == NULL) {
1576         tp = pathp;
1577         phase = 2;
1578     }
1579     if (tp == NULL) {
1580         tp = "";
1581     }
1582     haveComponent = 0;
1583     psp = NULL;
1584     tscp = rootSCachep;
1585     cm_HoldSCache(tscp);
1586     symlinkCount = 0;
1587     dirScp = NULL;
1588
1589
1590     while (1) {
1591         tc = *tp++;
1592
1593         /* map Unix slashes into DOS ones so we can interpret Unix
1594          * symlinks properly
1595          */
1596         if (tc == '/') 
1597             tc = '\\';
1598
1599         if (!haveComponent) {
1600             if (tc == '\\') {
1601                 continue;
1602             } else if (tc == 0) {
1603                 if (phase == 1) {
1604                     phase = 2;
1605                     tp = pathp;
1606                     continue;
1607                 }
1608                 code = 0;
1609                 break;
1610             } else {
1611                 haveComponent = 1;
1612                 cp = component;
1613                 *cp++ = tc;
1614             }
1615         } else {
1616             /* we have a component here */
1617             if (tc == 0 || tc == '\\') {
1618                 /* end of the component; we're at the last
1619                  * component if tc == 0.  However, if the last
1620                  * is a symlink, we have more to do.
1621                  */
1622                 *cp++ = 0;      /* add null termination */
1623                 if (!strcmp(".",component)) {
1624                     code = 0;
1625                     if (dirScp) {
1626                         cm_ReleaseSCache(dirScp);
1627                         dirScp = NULL;
1628                     }
1629                     break;
1630                 }
1631                 extraFlag = 0;
1632                 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1633                     extraFlag = CM_FLAG_NOMOUNTCHASE;
1634                 code = cm_Lookup(tscp, component,
1635                                   flags | extraFlag,
1636                                   userp, reqp, &nscp);
1637                 if (code) {
1638                     cm_ReleaseSCache(tscp);
1639                     if (dirScp)
1640                         cm_ReleaseSCache(dirScp);
1641                     if (psp) 
1642                         cm_FreeSpace(psp);
1643                     if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1644                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1645                         return CM_ERROR_NOSUCHPATH;
1646                     } else {
1647                         osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1648                         return code;
1649                     }
1650                 }       
1651                 haveComponent = 0;      /* component done */
1652                 if (dirScp)
1653                     cm_ReleaseSCache(dirScp);
1654                 dirScp = tscp;          /* for some symlinks */
1655                 tscp = nscp;            /* already held */
1656                 nscp = NULL;
1657                 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1658                     code = 0;
1659                     if (dirScp) {
1660                         cm_ReleaseSCache(dirScp);
1661                         dirScp = NULL;
1662                     }
1663                     break;
1664                 }
1665
1666                 /* now, if tscp is a symlink, we should follow
1667                  * it and assemble the path again.
1668                  */
1669                 lock_ObtainMutex(&tscp->mx);
1670                 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1671                                   CM_SCACHESYNC_GETSTATUS
1672                                   | CM_SCACHESYNC_NEEDCALLBACK);
1673                 if (code) {
1674                     lock_ReleaseMutex(&tscp->mx);
1675                     cm_ReleaseSCache(tscp);
1676                     tscp = NULL;
1677                     if (dirScp) {
1678                         cm_ReleaseSCache(dirScp);
1679                         dirScp = NULL;
1680                     }
1681                     break;
1682                 }
1683                 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1684
1685                 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1686                     /* this is a symlink; assemble a new buffer */
1687                     lock_ReleaseMutex(&tscp->mx);
1688                     if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1689                         cm_ReleaseSCache(tscp);
1690                         tscp = NULL;
1691                         if (dirScp) {
1692                             cm_ReleaseSCache(dirScp);
1693                             dirScp = NULL;
1694                         }
1695                         if (psp) 
1696                             cm_FreeSpace(psp);
1697                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
1698                         return CM_ERROR_TOO_MANY_SYMLINKS;
1699                     }
1700                     if (tc == 0) 
1701                         restp = "";
1702                     else 
1703                         restp = tp;
1704                     code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1705                     if (code) {
1706                         /* something went wrong */
1707                         cm_ReleaseSCache(tscp);
1708                         tscp = NULL;
1709                         if (dirScp) {
1710                             cm_ReleaseSCache(dirScp);
1711                             dirScp = NULL;
1712                         }
1713                         break;
1714                     }
1715
1716                     /* otherwise, tempsp has the new path,
1717                      * and linkScp is the new root from
1718                      * which to interpret that path.
1719                      * Continue with the namei processing,
1720                      * also doing the bookkeeping for the
1721                      * space allocation and tracking the
1722                      * vnode reference counts.
1723                      */
1724                     if (psp) 
1725                         cm_FreeSpace(psp);
1726                     psp = tempsp;
1727                     tp = psp->data;
1728                     cm_ReleaseSCache(tscp);
1729                     tscp = linkScp;
1730                     linkScp = NULL;
1731                     /* already held
1732                      * by AssembleLink
1733                      * now, if linkScp is null, that's
1734                      * AssembleLink's way of telling us that
1735                      * the sym link is relative to the dir
1736                      * containing the link.  We have a ref
1737                      * to it in dirScp, and we hold it now
1738                      * and reuse it as the new spot in the
1739                      * dir hierarchy.
1740                      */
1741                     if (tscp == NULL) {
1742                         tscp = dirScp;
1743                         dirScp = NULL;
1744                     }
1745                 } else {
1746                     /* not a symlink, we may be done */
1747                     lock_ReleaseMutex(&tscp->mx);
1748                     if (tc == 0) {
1749                         if (phase == 1) {
1750                             phase = 2;
1751                             tp = pathp;
1752                             continue;
1753                         }
1754                         if (dirScp) {
1755                             cm_ReleaseSCache(dirScp);
1756                             dirScp = NULL;
1757                         }
1758                         code = 0;
1759                         break;
1760                     }
1761                 }
1762                 if (dirScp) {
1763                     cm_ReleaseSCache(dirScp);
1764                     dirScp = NULL;
1765                 }
1766             } /* end of a component */
1767             else 
1768                 *cp++ = tc;
1769         } /* we have a component */
1770     } /* big while loop over all components */
1771
1772     /* already held */
1773     if (dirScp)
1774         cm_ReleaseSCache(dirScp);
1775     if (psp) 
1776         cm_FreeSpace(psp);
1777     if (code == 0) 
1778         *outScpp = tscp;
1779     else if (tscp)
1780         cm_ReleaseSCache(tscp);
1781
1782 #ifdef DEBUG_REFCOUNT
1783     afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp)->refCount);
1784 #endif
1785     osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
1786     return code;
1787 }
1788
1789 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1790  * We chase the link, and return a held pointer to the target, if it exists,
1791  * in *outScpp.  If we succeed, we return 0, otherwise we return an error code
1792  * and do not hold or return a target vnode.
1793  *
1794  * This is very similar to calling cm_NameI with the last component of a name,
1795  * which happens to be a symlink, except that we've already passed by the name.
1796  *
1797  * This function is typically called by the directory listing functions, which
1798  * encounter symlinks but need to return the proper file length so programs
1799  * like "more" work properly when they make use of the attributes retrieved from
1800  * the dir listing.
1801  *
1802  * The input vnode should not be locked when this function is called.
1803  */
1804 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1805                          cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1806 {
1807     long code;
1808     cm_space_t *spacep;
1809     cm_scache_t *newRootScp;
1810
1811     osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
1812
1813     code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1814     if (code) 
1815         return code;
1816
1817     /* now, if newRootScp is NULL, we're really being told that the symlink
1818      * is relative to the current directory (dscp).
1819      */
1820     if (newRootScp == NULL) {
1821         newRootScp = dscp;
1822         cm_HoldSCache(dscp);
1823     }
1824
1825     code = cm_NameI(newRootScp, spacep->data,
1826                      CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1827                      userp, NULL, reqp, outScpp);
1828
1829     if (code == CM_ERROR_NOSUCHFILE)
1830         code = CM_ERROR_NOSUCHPATH;
1831
1832     /* this stuff is allocated no matter what happened on the namei call,
1833      * so free it */
1834     cm_FreeSpace(spacep);
1835     cm_ReleaseSCache(newRootScp);
1836
1837     return code;
1838 }
1839
1840 /* make this big enough so that one buffer of dir pages won't overflow.  We'll
1841  * check anyway, but we want to minimize the chance that we have to leave stuff
1842  * unstat'd.
1843  */
1844 #define CM_BULKMAX              (3 * AFSCBMAX)
1845
1846 /* rock for bulk stat calls */
1847 typedef struct cm_bulkStat {
1848     osi_hyper_t bufOffset;      /* only do it for things in this buffer page */
1849
1850     /* info for the actual call */
1851     int counter;                        /* next free slot */
1852     AFSFid fids[CM_BULKMAX];
1853     AFSFetchStatus stats[CM_BULKMAX];
1854     AFSCallBack callbacks[CM_BULKMAX];
1855 } cm_bulkStat_t;
1856
1857 /* for a given entry, make sure that it isn't in the stat cache, and then
1858  * add it to the list of file IDs to be obtained.
1859  *
1860  * Don't bother adding it if we already have a vnode.  Note that the dir
1861  * is locked, so we have to be careful checking the vnode we're thinking of
1862  * processing, to avoid deadlocks.
1863  */
1864 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1865                      osi_hyper_t *offp)
1866 {
1867     osi_hyper_t thyper;
1868     cm_bulkStat_t *bsp;
1869     int i;
1870     cm_scache_t *tscp;
1871     cm_fid_t tfid;
1872
1873     bsp = rockp;
1874
1875     /* Don't overflow bsp. */
1876     if (bsp->counter >= CM_BULKMAX)
1877         return CM_ERROR_STOPNOW;
1878
1879     thyper.LowPart = cm_data.buf_blockSize;
1880     thyper.HighPart = 0;
1881     thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1882
1883     /* thyper is now the first byte past the end of the record we're
1884      * interested in, and bsp->bufOffset is the first byte of the record
1885      * we're interested in.
1886      * Skip data in the others.
1887      * Skip '.' and '..'
1888      */
1889     if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1890         return 0;
1891     if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1892         return CM_ERROR_STOPNOW;
1893     if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1894         return 0;
1895
1896     tfid.cell = scp->fid.cell;
1897     tfid.volume = scp->fid.volume;
1898     tfid.vnode = ntohl(dep->fid.vnode);
1899     tfid.unique = ntohl(dep->fid.unique);
1900     tscp = cm_FindSCache(&tfid);
1901     if (tscp) {
1902         if (lock_TryMutex(&tscp->mx)) {
1903             /* we have an entry that we can look at */
1904             if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
1905                 /* we have a callback on it.  Don't bother
1906                  * fetching this stat entry, since we're happy
1907                  * with the info we have.
1908                  */
1909                 lock_ReleaseMutex(&tscp->mx);
1910                 cm_ReleaseSCache(tscp);
1911                 return 0;
1912             }
1913             lock_ReleaseMutex(&tscp->mx);
1914         }       /* got lock */
1915         cm_ReleaseSCache(tscp);
1916     }   /* found entry */
1917
1918 #ifdef AFS_FREELANCE_CLIENT
1919     // yj: if this is a mountpoint under root.afs then we don't want it
1920     // to be bulkstat-ed, instead, we call getSCache directly and under
1921     // getSCache, it is handled specially.
1922     if  ( cm_freelanceEnabled &&
1923           tfid.cell==AFS_FAKE_ROOT_CELL_ID && 
1924           tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1925           !(tfid.vnode==0x1 && tfid.unique==0x1) )
1926     {       
1927         osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1928         return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1929     }
1930 #endif /* AFS_FREELANCE_CLIENT */
1931
1932     i = bsp->counter++;
1933     bsp->fids[i].Volume = scp->fid.volume;
1934     bsp->fids[i].Vnode = tfid.vnode;
1935     bsp->fids[i].Unique = tfid.unique;
1936     return 0;
1937 }       
1938
1939 /* called with a locked scp and a pointer to a buffer.  Make bulk stat
1940  * calls on all undeleted files in the page of the directory specified.
1941  */
1942 afs_int32
1943 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1944                cm_req_t *reqp)
1945 {
1946     long code;
1947     cm_bulkStat_t bb;   /* this is *BIG*, probably 16K or so;
1948                          * watch for stack problems */
1949     AFSCBFids fidStruct;
1950     AFSBulkStats statStruct;
1951     cm_conn_t *connp;
1952     AFSCBs callbackStruct;
1953     long filex;
1954     AFSVolSync volSync;
1955     cm_callbackRequest_t cbReq;
1956     long filesThisCall;
1957     long i;
1958     long j;
1959     cm_scache_t *scp;
1960     cm_fid_t tfid;
1961     struct rx_connection * callp;
1962     int inlinebulk = 0;         /* Did we use InlineBulkStatus RPC or not? */
1963
1964     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
1965
1966     /* should be on a buffer boundary */
1967     osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
1968
1969     memset(&bb, 0, sizeof(bb));
1970     bb.bufOffset = *offsetp;
1971
1972     lock_ReleaseMutex(&dscp->mx);
1973     /* first, assemble the file IDs we need to stat */
1974     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
1975
1976     /* if we failed, bail out early */
1977     if (code && code != CM_ERROR_STOPNOW) {
1978         lock_ObtainMutex(&dscp->mx);
1979         return code;
1980     }
1981
1982     /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1983      * make the calls to create the entries.  Handle AFSCBMAX files at a
1984      * time.
1985      */
1986     filex = 0;
1987     while (filex < bb.counter) {
1988         filesThisCall = bb.counter - filex;
1989         if (filesThisCall > AFSCBMAX) 
1990             filesThisCall = AFSCBMAX;
1991
1992         fidStruct.AFSCBFids_len = filesThisCall;
1993         fidStruct.AFSCBFids_val = &bb.fids[filex];
1994         statStruct.AFSBulkStats_len = filesThisCall;
1995         statStruct.AFSBulkStats_val = &bb.stats[filex];
1996         callbackStruct.AFSCBs_len = filesThisCall;
1997         callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1998         cm_StartCallbackGrantingCall(NULL, &cbReq);
1999         osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2000         do {
2001             code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2002             if (code) 
2003                 continue;
2004
2005             callp = cm_GetRxConn(connp);
2006             if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2007                 code = RXAFS_InlineBulkStatus(callp, &fidStruct,
2008                                      &statStruct, &callbackStruct, &volSync);
2009                 if (code == RXGEN_OPCODE) {
2010                     cm_SetServerNoInlineBulk(connp->serverp, 0);
2011                 } else {
2012                     inlinebulk = 1;
2013                 }
2014             }
2015             if (!inlinebulk) {
2016                 code = RXAFS_BulkStatus(callp, &fidStruct,
2017                                         &statStruct, &callbackStruct, &volSync);
2018             }
2019             rx_PutConnection(callp);
2020
2021         } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2022                              &volSync, NULL, &cbReq, code));
2023         code = cm_MapRPCError(code, reqp);
2024         if (code)
2025             osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x", 
2026                       inlinebulk ? "Inline" : "", code);
2027         else
2028             osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2029
2030         /* may as well quit on an error, since we're not going to do
2031          * much better on the next immediate call, either.
2032          */
2033         if (code) {
2034             cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2035             break;
2036         }
2037
2038         /* otherwise, we should do the merges */
2039         for (i = 0; i<filesThisCall; i++) {
2040             j = filex + i;
2041             tfid.cell = dscp->fid.cell;
2042             tfid.volume = bb.fids[j].Volume;
2043             tfid.vnode = bb.fids[j].Vnode;
2044             tfid.unique = bb.fids[j].Unique;
2045             code = cm_GetSCache(&tfid, &scp, userp, reqp);
2046             if (code != 0) 
2047                 continue;
2048
2049             /* otherwise, if this entry has no callback info, 
2050              * merge in this.
2051              */
2052             lock_ObtainMutex(&scp->mx);
2053             /* now, we have to be extra paranoid on merging in this
2054              * information, since we didn't use cm_SyncOp before
2055              * starting the fetch to make sure that no bad races
2056              * were occurring.  Specifically, we need to make sure
2057              * we don't obliterate any newer information in the
2058              * vnode than have here.
2059              *
2060              * Right now, be pretty conservative: if there's a
2061              * callback or a pending call, skip it.
2062              */
2063             if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2064                  && !(scp->flags &
2065                        (CM_SCACHEFLAG_FETCHING
2066                          | CM_SCACHEFLAG_STORING
2067                          | CM_SCACHEFLAG_SIZESTORING))) {
2068                 cm_EndCallbackGrantingCall(scp, &cbReq,
2069                                             &bb.callbacks[j],
2070                                             CM_CALLBACK_MAINTAINCOUNT);
2071                 cm_MergeStatus(scp, &bb.stats[j], &volSync, userp, 0);
2072             }       
2073             lock_ReleaseMutex(&scp->mx);
2074             cm_ReleaseSCache(scp);
2075         } /* all files in the response */
2076         /* now tell it to drop the count,
2077          * after doing the vnode processing above */
2078         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2079
2080         filex += filesThisCall;
2081     }   /* while there are still more files to process */
2082     lock_ObtainMutex(&dscp->mx);
2083
2084 #if 0
2085     /* If we did the InlineBulk RPC pull out the return code */
2086     if (inlinebulk) {
2087         if ((&bb.stats[0])->errorCode) {
2088             cm_Analyze(NULL /*connp was released by the previous cm_Analyze */, 
2089                         userp, reqp, &dscp->fid, &volSync, NULL, NULL, (&bb.stats[0])->errorCode);
2090             code = cm_MapRPCError((&bb.stats[0])->errorCode, reqp);
2091         }
2092     } else
2093 #endif  
2094     { 
2095         code = 0;
2096     }
2097
2098     osi_Log1(afsd_logp, "END cm_TryBulkStat code = 0x%x", code);
2099     return code;
2100 }       
2101
2102 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2103 {
2104     long mask;
2105
2106     /* initialize store back mask as inexpensive local variable */
2107     mask = 0;
2108     memset(statusp, 0, sizeof(AFSStoreStatus));
2109
2110     /* copy out queued info from scache first, if scp passed in */
2111     if (scp) {
2112         if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2113             statusp->ClientModTime = scp->clientModTime;
2114             mask |= AFS_SETMODTIME;
2115             scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2116         }
2117     }
2118
2119     if (attrp) {
2120         /* now add in our locally generated request */
2121         if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2122             statusp->ClientModTime = attrp->clientModTime;
2123             mask |= AFS_SETMODTIME;
2124         }
2125         if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2126             statusp->UnixModeBits = attrp->unixModeBits;
2127             mask |= AFS_SETMODE;
2128         }
2129         if (attrp->mask & CM_ATTRMASK_OWNER) {
2130             statusp->Owner = attrp->owner;
2131             mask |= AFS_SETOWNER;
2132         }
2133         if (attrp->mask & CM_ATTRMASK_GROUP) {
2134             statusp->Group = attrp->group;
2135             mask |= AFS_SETGROUP;
2136         }
2137     }
2138     statusp->Mask = mask;
2139 }       
2140
2141 /* set the file size, and make sure that all relevant buffers have been
2142  * truncated.  Ensure that any partially truncated buffers have been zeroed
2143  * to the end of the buffer.
2144  */
2145 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2146                    cm_req_t *reqp)
2147 {
2148     long code;
2149     int shrinking;
2150
2151     /* start by locking out buffer creation */
2152     lock_ObtainWrite(&scp->bufCreateLock);
2153
2154     /* verify that this is a file, not a dir or a symlink */
2155     lock_ObtainMutex(&scp->mx);
2156     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2157                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2158     if (code) 
2159         goto done;
2160     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2161
2162     if (scp->fileType != CM_SCACHETYPE_FILE) {
2163         code = CM_ERROR_ISDIR;
2164         goto done;
2165     }
2166
2167   startover:
2168     if (LargeIntegerLessThan(*sizep, scp->length))
2169         shrinking = 1;
2170     else
2171         shrinking = 0;
2172
2173     lock_ReleaseMutex(&scp->mx);
2174
2175     /* can't hold scp->mx lock here, since we may wait for a storeback to
2176      * finish if the buffer package is cleaning a buffer by storing it to
2177      * the server.
2178      */
2179     if (shrinking)
2180         buf_Truncate(scp, userp, reqp, sizep);
2181
2182     /* now ensure that file length is short enough, and update truncPos */
2183     lock_ObtainMutex(&scp->mx);
2184
2185     /* make sure we have a callback (so we have the right value for the
2186      * length), and wait for it to be safe to do a truncate.
2187      */
2188     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2189                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2190                       | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2191     if (code) 
2192         goto done;
2193
2194     if (LargeIntegerLessThan(*sizep, scp->length)) {
2195         /* a real truncation.  If truncPos is not set yet, or is bigger
2196          * than where we're truncating the file, set truncPos to this
2197          * new value.
2198          */
2199         if (!shrinking)
2200             goto startover;
2201         if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2202              || LargeIntegerLessThan(*sizep, scp->length)) {
2203             /* set trunc pos */
2204             scp->truncPos = *sizep;
2205             scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2206         }
2207         /* in either case, the new file size has been changed */
2208         scp->length = *sizep;
2209         scp->mask |= CM_SCACHEMASK_LENGTH;
2210     }
2211     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2212         /* really extending the file */
2213         scp->length = *sizep;
2214         scp->mask |= CM_SCACHEMASK_LENGTH;
2215     }
2216
2217     /* done successfully */
2218     code = 0;
2219
2220     cm_SyncOpDone(scp, NULL, 
2221                    CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2222                    | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2223
2224   done:
2225     lock_ReleaseMutex(&scp->mx);
2226     lock_ReleaseWrite(&scp->bufCreateLock);
2227
2228     return code;
2229 }
2230
2231 /* set the file size or other attributes (but not both at once) */
2232 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2233                 cm_req_t *reqp)
2234 {
2235     long code;
2236     AFSFetchStatus afsOutStatus;
2237     AFSVolSync volSync;
2238     cm_conn_t *connp;
2239     AFSFid tfid;
2240     AFSStoreStatus afsInStatus;
2241     struct rx_connection * callp;
2242
2243     /* handle file length setting */
2244     if (attrp->mask & CM_ATTRMASK_LENGTH)
2245         return cm_SetLength(scp, &attrp->length, userp, reqp);
2246
2247     lock_ObtainMutex(&scp->mx);
2248     /* otherwise, we have to make an RPC to get the status */
2249     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2250     if (code) {
2251         lock_ReleaseMutex(&scp->mx);
2252         return code;
2253     }
2254
2255     /* make the attr structure */
2256     cm_StatusFromAttr(&afsInStatus, scp, attrp);
2257
2258     tfid.Volume = scp->fid.volume;
2259     tfid.Vnode = scp->fid.vnode;
2260     tfid.Unique = scp->fid.unique;
2261         lock_ReleaseMutex(&scp->mx);
2262
2263     /* now make the RPC */
2264     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2265     do {
2266         code = cm_Conn(&scp->fid, userp, reqp, &connp);
2267         if (code) 
2268             continue;
2269
2270         callp = cm_GetRxConn(connp);
2271         code = RXAFS_StoreStatus(callp, &tfid,
2272                                   &afsInStatus, &afsOutStatus, &volSync);
2273         rx_PutConnection(callp);
2274
2275     } while (cm_Analyze(connp, userp, reqp,
2276                          &scp->fid, &volSync, NULL, NULL, code));
2277     code = cm_MapRPCError(code, reqp);
2278
2279     if (code)
2280         osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2281     else
2282         osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2283
2284     lock_ObtainMutex(&scp->mx);
2285     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2286     if (code == 0)
2287         cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
2288                         CM_MERGEFLAG_FORCE);
2289         
2290     /* if we're changing the mode bits, discard the ACL cache, 
2291      * since we changed the mode bits.
2292      */
2293     if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
2294     lock_ReleaseMutex(&scp->mx);
2295     return code;
2296 }       
2297
2298 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2299                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2300 {       
2301     cm_conn_t *connp;
2302     long code;
2303     AFSFid dirAFSFid;
2304     cm_callbackRequest_t cbReq;
2305     AFSFid newAFSFid;
2306     cm_fid_t newFid;
2307     cm_scache_t *scp;
2308     int didEnd;
2309     AFSStoreStatus inStatus;
2310     AFSFetchStatus updatedDirStatus;
2311     AFSFetchStatus newFileStatus;
2312     AFSCallBack newFileCallback;
2313     AFSVolSync volSync;
2314     struct rx_connection * callp;
2315
2316     /* can't create names with @sys in them; must expand it manually first.
2317      * return "invalid request" if they try.
2318      */
2319     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2320         return CM_ERROR_ATSYS;
2321     }
2322
2323     /* before starting the RPC, mark that we're changing the file data, so
2324      * that someone who does a chmod will know to wait until our call
2325      * completes.
2326      */
2327     lock_ObtainMutex(&dscp->mx);
2328     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2329     if (code == 0) {
2330         cm_StartCallbackGrantingCall(NULL, &cbReq);
2331     }
2332     lock_ReleaseMutex(&dscp->mx);
2333     if (code) {
2334         return code;
2335     }
2336     didEnd = 0;
2337
2338     cm_StatusFromAttr(&inStatus, NULL, attrp);
2339
2340     /* try the RPC now */
2341     osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2342     do {
2343         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2344         if (code) 
2345             continue;
2346
2347         dirAFSFid.Volume = dscp->fid.volume;
2348         dirAFSFid.Vnode = dscp->fid.vnode;
2349         dirAFSFid.Unique = dscp->fid.unique;
2350
2351         callp = cm_GetRxConn(connp);
2352         code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2353                                  &inStatus, &newAFSFid, &newFileStatus,
2354                                  &updatedDirStatus, &newFileCallback,
2355                                  &volSync);
2356         rx_PutConnection(callp);
2357
2358     } while (cm_Analyze(connp, userp, reqp,
2359                          &dscp->fid, &volSync, NULL, &cbReq, code));
2360     code = cm_MapRPCError(code, reqp);
2361         
2362     if (code)
2363         osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2364     else
2365         osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2366
2367     lock_ObtainMutex(&dscp->mx);
2368     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2369     if (code == 0) {
2370         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2371     }
2372     lock_ReleaseMutex(&dscp->mx);
2373
2374     /* now try to create the file's entry, too, but be careful to 
2375      * make sure that we don't merge in old info.  Since we weren't locking
2376      * out any requests during the file's creation, we may have pretty old
2377      * info.
2378      */
2379     if (code == 0) {
2380         newFid.cell = dscp->fid.cell;
2381         newFid.volume = dscp->fid.volume;
2382         newFid.vnode = newAFSFid.Vnode;
2383         newFid.unique = newAFSFid.Unique;
2384         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2385         if (code == 0) {
2386             lock_ObtainMutex(&scp->mx);
2387             scp->creator = userp;               /* remember who created it */
2388             if (!cm_HaveCallback(scp)) {
2389                 cm_MergeStatus(scp, &newFileStatus, &volSync,
2390                                 userp, 0);
2391                 cm_EndCallbackGrantingCall(scp, &cbReq,
2392                                             &newFileCallback, 0);
2393                 didEnd = 1;     
2394             }       
2395             lock_ReleaseMutex(&scp->mx);
2396             *scpp = scp;
2397         }
2398     }
2399
2400     /* make sure we end things properly */
2401     if (!didEnd)
2402         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2403
2404     return code;
2405 }       
2406
2407 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2408 {
2409     long code;
2410
2411     lock_ObtainWrite(&scp->bufCreateLock);
2412     code = buf_CleanVnode(scp, userp, reqp);
2413     lock_ReleaseWrite(&scp->bufCreateLock);
2414     if (code == 0) {
2415         lock_ObtainMutex(&scp->mx);
2416
2417         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2418                           | CM_SCACHEMASK_CLIENTMODTIME
2419                           | CM_SCACHEMASK_LENGTH))
2420             code = cm_StoreMini(scp, userp, reqp);
2421
2422         if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2423             code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2424             scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2425         }
2426
2427         lock_ReleaseMutex(&scp->mx);
2428     }
2429     return code;
2430 }
2431
2432 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2433                  cm_user_t *userp, cm_req_t *reqp)
2434 {
2435     cm_conn_t *connp;
2436     long code;
2437     AFSFid dirAFSFid;
2438     cm_callbackRequest_t cbReq;
2439     AFSFid newAFSFid;
2440     cm_fid_t newFid;
2441     cm_scache_t *scp;
2442     int didEnd;
2443     AFSStoreStatus inStatus;
2444     AFSFetchStatus updatedDirStatus;
2445     AFSFetchStatus newDirStatus;
2446     AFSCallBack newDirCallback;
2447     AFSVolSync volSync;
2448     struct rx_connection * callp;
2449
2450     /* can't create names with @sys in them; must expand it manually first.
2451      * return "invalid request" if they try.
2452      */
2453     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2454         return CM_ERROR_ATSYS;
2455     }
2456
2457     /* before starting the RPC, mark that we're changing the directory
2458      * data, so that someone who does a chmod on the dir will wait until
2459      * our call completes.
2460      */
2461     lock_ObtainMutex(&dscp->mx);
2462     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2463     if (code == 0) {
2464         cm_StartCallbackGrantingCall(NULL, &cbReq);
2465     }
2466     lock_ReleaseMutex(&dscp->mx);
2467     if (code) {
2468         return code;
2469     }
2470     didEnd = 0;
2471
2472     cm_StatusFromAttr(&inStatus, NULL, attrp);
2473
2474     /* try the RPC now */
2475     osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2476     do {
2477         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2478         if (code) 
2479             continue;
2480
2481         dirAFSFid.Volume = dscp->fid.volume;
2482         dirAFSFid.Vnode = dscp->fid.vnode;
2483         dirAFSFid.Unique = dscp->fid.unique;
2484
2485         callp = cm_GetRxConn(connp);
2486         code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2487                               &inStatus, &newAFSFid, &newDirStatus,
2488                               &updatedDirStatus, &newDirCallback,
2489                               &volSync);
2490         rx_PutConnection(callp);
2491
2492     } while (cm_Analyze(connp, userp, reqp,
2493                          &dscp->fid, &volSync, NULL, &cbReq, code));
2494     code = cm_MapRPCError(code, reqp);
2495         
2496     if (code)
2497         osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2498     else
2499         osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2500
2501     lock_ObtainMutex(&dscp->mx);
2502     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2503     if (code == 0) {
2504         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2505     }
2506     lock_ReleaseMutex(&dscp->mx);
2507
2508     /* now try to create the new dir's entry, too, but be careful to 
2509      * make sure that we don't merge in old info.  Since we weren't locking
2510      * out any requests during the file's creation, we may have pretty old
2511      * info.
2512      */
2513     if (code == 0) {
2514         newFid.cell = dscp->fid.cell;
2515         newFid.volume = dscp->fid.volume;
2516         newFid.vnode = newAFSFid.Vnode;
2517         newFid.unique = newAFSFid.Unique;
2518         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2519         if (code == 0) {
2520             lock_ObtainMutex(&scp->mx);
2521             if (!cm_HaveCallback(scp)) {
2522                 cm_MergeStatus(scp, &newDirStatus, &volSync,
2523                                 userp, 0);
2524                 cm_EndCallbackGrantingCall(scp, &cbReq,
2525                                             &newDirCallback, 0);
2526                 didEnd = 1;             
2527             }
2528             lock_ReleaseMutex(&scp->mx);
2529             cm_ReleaseSCache(scp);
2530         }
2531     }
2532
2533     /* make sure we end things properly */
2534     if (!didEnd)
2535         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2536
2537     /* and return error code */
2538     return code;
2539 }       
2540
2541 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2542              cm_user_t *userp, cm_req_t *reqp)
2543 {
2544     cm_conn_t *connp;
2545     long code = 0;
2546     AFSFid dirAFSFid;
2547     AFSFid existingAFSFid;
2548     AFSFetchStatus updatedDirStatus;
2549     AFSFetchStatus newLinkStatus;
2550     AFSVolSync volSync;
2551     struct rx_connection * callp;
2552
2553     if (dscp->fid.cell != sscp->fid.cell ||
2554         dscp->fid.volume != sscp->fid.volume) {
2555         return CM_ERROR_CROSSDEVLINK;
2556     }
2557
2558     lock_ObtainMutex(&dscp->mx);
2559     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2560     lock_ReleaseMutex(&dscp->mx);
2561
2562     if (code)
2563         return code;
2564
2565     /* try the RPC now */
2566     osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2567     do {
2568         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2569         if (code) continue;
2570
2571         dirAFSFid.Volume = dscp->fid.volume;
2572         dirAFSFid.Vnode = dscp->fid.vnode;
2573         dirAFSFid.Unique = dscp->fid.unique;
2574
2575         existingAFSFid.Volume = sscp->fid.volume;
2576         existingAFSFid.Vnode = sscp->fid.vnode;
2577         existingAFSFid.Unique = sscp->fid.unique;
2578
2579         callp = cm_GetRxConn(connp);
2580         code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2581             &newLinkStatus, &updatedDirStatus, &volSync);
2582         rx_PutConnection(callp);
2583         osi_Log1(smb_logp,"  RXAFS_Link returns 0x%x", code);
2584
2585     } while (cm_Analyze(connp, userp, reqp,
2586         &dscp->fid, &volSync, NULL, NULL, code));
2587
2588     code = cm_MapRPCError(code, reqp);
2589
2590     if (code)
2591         osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2592     else
2593         osi_Log0(afsd_logp, "CALL Link SUCCESS");
2594
2595     lock_ObtainMutex(&dscp->mx);
2596     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2597     if (code == 0) {
2598         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2599     }
2600     lock_ReleaseMutex(&dscp->mx);
2601
2602     return code;
2603 }
2604
2605 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2606                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2607 {
2608     cm_conn_t *connp;
2609     long code;
2610     AFSFid dirAFSFid;
2611     AFSFid newAFSFid;
2612     cm_fid_t newFid;
2613     cm_scache_t *scp;
2614     AFSStoreStatus inStatus;
2615     AFSFetchStatus updatedDirStatus;
2616     AFSFetchStatus newLinkStatus;
2617     AFSVolSync volSync;
2618     struct rx_connection * callp;
2619
2620     /* before starting the RPC, mark that we're changing the directory data,
2621      * so that someone who does a chmod on the dir will wait until our
2622      * call completes.
2623      */
2624     lock_ObtainMutex(&dscp->mx);
2625     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2626     lock_ReleaseMutex(&dscp->mx);
2627     if (code) {
2628         return code;
2629     }
2630
2631     cm_StatusFromAttr(&inStatus, NULL, attrp);
2632
2633     /* try the RPC now */
2634     osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
2635     do {
2636         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2637         if (code) 
2638             continue;
2639
2640         dirAFSFid.Volume = dscp->fid.volume;
2641         dirAFSFid.Vnode = dscp->fid.vnode;
2642         dirAFSFid.Unique = dscp->fid.unique;
2643
2644         callp = cm_GetRxConn(connp);
2645         code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2646                               &inStatus, &newAFSFid, &newLinkStatus,
2647                               &updatedDirStatus, &volSync);
2648         rx_PutConnection(callp);
2649
2650     } while (cm_Analyze(connp, userp, reqp,
2651                          &dscp->fid, &volSync, NULL, NULL, code));
2652     code = cm_MapRPCError(code, reqp);
2653         
2654     if (code)
2655         osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
2656     else
2657         osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
2658
2659     lock_ObtainMutex(&dscp->mx);
2660     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2661     if (code == 0) {
2662         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2663     }
2664     lock_ReleaseMutex(&dscp->mx);
2665
2666     /* now try to create the new dir's entry, too, but be careful to 
2667      * make sure that we don't merge in old info.  Since we weren't locking
2668      * out any requests during the file's creation, we may have pretty old
2669      * info.
2670      */
2671     if (code == 0) {
2672         newFid.cell = dscp->fid.cell;
2673         newFid.volume = dscp->fid.volume;
2674         newFid.vnode = newAFSFid.Vnode;
2675         newFid.unique = newAFSFid.Unique;
2676         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2677         if (code == 0) {
2678             lock_ObtainMutex(&scp->mx);
2679             if (!cm_HaveCallback(scp)) {
2680                 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2681                                 userp, 0);
2682             }       
2683             lock_ReleaseMutex(&scp->mx);
2684             cm_ReleaseSCache(scp);
2685         }
2686     }
2687         
2688     /* and return error code */
2689     return code;
2690 }
2691
2692 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2693                    cm_req_t *reqp)
2694 {
2695     cm_conn_t *connp;
2696     long code;
2697     AFSFid dirAFSFid;
2698     int didEnd;
2699     AFSFetchStatus updatedDirStatus;
2700     AFSVolSync volSync;
2701     struct rx_connection * callp;
2702
2703     /* before starting the RPC, mark that we're changing the directory data,
2704      * so that someone who does a chmod on the dir will wait until our
2705      * call completes.
2706      */
2707     lock_ObtainMutex(&dscp->mx);
2708     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2709     lock_ReleaseMutex(&dscp->mx);
2710     if (code) {
2711         return code;
2712     }
2713     didEnd = 0;
2714
2715     /* try the RPC now */
2716     osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
2717     do {
2718         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2719         if (code) 
2720             continue;
2721
2722         dirAFSFid.Volume = dscp->fid.volume;
2723         dirAFSFid.Vnode = dscp->fid.vnode;
2724         dirAFSFid.Unique = dscp->fid.unique;
2725
2726         callp = cm_GetRxConn(connp);
2727         code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2728                                 &updatedDirStatus, &volSync);
2729         rx_PutConnection(callp);
2730
2731     } while (cm_Analyze(connp, userp, reqp,
2732                          &dscp->fid, &volSync, NULL, NULL, code));
2733     code = cm_MapRPCErrorRmdir(code, reqp);
2734
2735     if (code)
2736         osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
2737     else
2738         osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
2739
2740     lock_ObtainMutex(&dscp->mx);
2741     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2742     if (code == 0) {
2743         cm_dnlcRemove(dscp, namep); 
2744         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2745     }
2746     lock_ReleaseMutex(&dscp->mx);
2747
2748     /* and return error code */
2749     return code;
2750 }
2751
2752 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2753 {
2754     /* grab mutex on contents */
2755     lock_ObtainMutex(&scp->mx);
2756
2757     /* reset the prefetch info */
2758     scp->prefetch.base.LowPart = 0;             /* base */
2759     scp->prefetch.base.HighPart = 0;
2760     scp->prefetch.end.LowPart = 0;              /* and end */
2761     scp->prefetch.end.HighPart = 0;
2762
2763     /* release mutex on contents */
2764     lock_ReleaseMutex(&scp->mx);
2765
2766     /* we're done */
2767     return 0;
2768 }       
2769
2770 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2771                 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2772 {
2773     cm_conn_t *connp;
2774     long code;
2775     AFSFid oldDirAFSFid;
2776     AFSFid newDirAFSFid;
2777     int didEnd;
2778     AFSFetchStatus updatedOldDirStatus;
2779     AFSFetchStatus updatedNewDirStatus;
2780     AFSVolSync volSync;
2781     int oneDir;
2782     struct rx_connection * callp;
2783
2784     /* before starting the RPC, mark that we're changing the directory data,
2785      * so that someone who does a chmod on the dir will wait until our call
2786      * completes.  We do this in vnode order so that we don't deadlock,
2787      * which makes the code a little verbose.
2788      */
2789     if (oldDscp == newDscp) {
2790         /* check for identical names */
2791         if (strcmp(oldNamep, newNamep) == 0)
2792             return CM_ERROR_RENAME_IDENTICAL;
2793
2794         oneDir = 1;
2795         lock_ObtainMutex(&oldDscp->mx);
2796         cm_dnlcRemove(oldDscp, oldNamep);
2797         cm_dnlcRemove(oldDscp, newNamep);
2798         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2799                           CM_SCACHESYNC_STOREDATA);
2800         lock_ReleaseMutex(&oldDscp->mx);
2801     }
2802     else {
2803         /* two distinct dir vnodes */
2804         oneDir = 0;
2805         if (oldDscp->fid.cell != newDscp->fid.cell ||
2806              oldDscp->fid.volume != newDscp->fid.volume)
2807             return CM_ERROR_CROSSDEVLINK;
2808
2809         /* shouldn't happen that we have distinct vnodes for two
2810          * different files, but could due to deliberate attack, or
2811          * stale info.  Avoid deadlocks and quit now.
2812          */
2813         if (oldDscp->fid.vnode == newDscp->fid.vnode)
2814             return CM_ERROR_CROSSDEVLINK;
2815
2816         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2817             lock_ObtainMutex(&oldDscp->mx);
2818             cm_dnlcRemove(oldDscp, oldNamep);
2819             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2820                               CM_SCACHESYNC_STOREDATA);
2821             lock_ReleaseMutex(&oldDscp->mx);
2822             if (code == 0) {
2823                 lock_ObtainMutex(&newDscp->mx);
2824                 cm_dnlcRemove(newDscp, newNamep);
2825                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2826                                   CM_SCACHESYNC_STOREDATA);
2827                 lock_ReleaseMutex(&newDscp->mx);
2828                 if (code) {
2829                     /* cleanup first one */
2830                     lock_ObtainMutex(&oldDscp->mx);
2831                     cm_SyncOpDone(oldDscp, NULL,
2832                                    CM_SCACHESYNC_STOREDATA);
2833                     lock_ReleaseMutex(&oldDscp->mx);
2834                 }       
2835             }
2836         }
2837         else {
2838             /* lock the new vnode entry first */
2839             lock_ObtainMutex(&newDscp->mx);
2840             cm_dnlcRemove(newDscp, newNamep);
2841             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2842                               CM_SCACHESYNC_STOREDATA);
2843             lock_ReleaseMutex(&newDscp->mx);
2844             if (code == 0) {
2845                 lock_ObtainMutex(&oldDscp->mx);
2846                 cm_dnlcRemove(oldDscp, oldNamep);
2847                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2848                                   CM_SCACHESYNC_STOREDATA);
2849                 lock_ReleaseMutex(&oldDscp->mx);
2850                 if (code) {
2851                     /* cleanup first one */
2852                     lock_ObtainMutex(&newDscp->mx);
2853                     cm_SyncOpDone(newDscp, NULL,
2854                                    CM_SCACHESYNC_STOREDATA);
2855                     lock_ReleaseMutex(&newDscp->mx);
2856                 }       
2857             }
2858         }
2859     }   /* two distinct vnodes */
2860
2861     if (code) {
2862         return code;
2863     }
2864     didEnd = 0;
2865
2866     /* try the RPC now */
2867     osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p", 
2868               oldDscp, newDscp);
2869     do {
2870         code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2871         if (code) 
2872             continue;
2873
2874         oldDirAFSFid.Volume = oldDscp->fid.volume;
2875         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2876         oldDirAFSFid.Unique = oldDscp->fid.unique;
2877         newDirAFSFid.Volume = newDscp->fid.volume;
2878         newDirAFSFid.Vnode = newDscp->fid.vnode;
2879         newDirAFSFid.Unique = newDscp->fid.unique;
2880
2881         callp = cm_GetRxConn(connp);
2882         code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2883                              &newDirAFSFid, newNamep,
2884                              &updatedOldDirStatus, &updatedNewDirStatus,
2885                              &volSync);
2886         rx_PutConnection(callp);
2887
2888     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2889                          &volSync, NULL, NULL, code));
2890     code = cm_MapRPCError(code, reqp);
2891         
2892     if (code)
2893         osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2894     else
2895         osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2896
2897     /* update the individual stat cache entries for the directories */
2898     lock_ObtainMutex(&oldDscp->mx);
2899     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2900     if (code == 0) {
2901         cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2902                         userp, 0);
2903     }
2904     lock_ReleaseMutex(&oldDscp->mx);
2905
2906     /* and update it for the new one, too, if necessary */
2907     if (!oneDir) {
2908         lock_ObtainMutex(&newDscp->mx);
2909         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2910         if (code == 0) {
2911             cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2912                             userp, 0);
2913         }
2914         lock_ReleaseMutex(&newDscp->mx);
2915     }
2916
2917     /* and return error code */
2918     return code;
2919 }
2920
2921 /* Byte range locks:
2922
2923    The OpenAFS Windows client has to fake byte range locks given no
2924    server side support for such locks.  This is implemented as keyed
2925    byte range locks on the cache manager.
2926
2927    Keyed byte range locks:
2928
2929    Each cm_scache_t structure keeps track of a list of keyed locks.
2930    The key for a lock identifies an owner of a set of locks (referred
2931    to as a client).  Each key is represented by a value.  The set of
2932    key values used within a specific cm_scache_t structure form a
2933    namespace that has a scope of just that cm_scache_t structure.  The
2934    same key value can be used with another cm_scache_t structure and
2935    correspond to a completely different client.  However it is
2936    advantageous for the SMB or IFS layer to make sure that there is a
2937    1-1 mapping between client and keys over all cm_scache_t objects.
2938
2939    Assume a client C has key Key(C) (although, since the scope of the
2940    key is a cm_scache_t, the key can be Key(C,S), where S is the
2941    cm_scache_t.  But assume a 1-1 relation between keys and clients).
2942    A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
2943    inclusive (a.k.a. [O,O+L-1]).  The function Key(x) is implemented
2944    through cm_generateKey() function for both SMB and IFS.
2945
2946    The list of locks for a cm_scache_t object S is maintained in
2947    S->fileLocks.  The cache manager will set a lock on the AFS file
2948    server in order to assert the locks in S->fileLocks.  If only
2949    shared locks are in place for S, then the cache manager will obtain
2950    a LockRead lock, while if there are any exclusive locks, it will
2951    obtain a LockWrite lock.  If the exclusive locks are all released
2952    while the shared locks remain, then the cache manager will
2953    downgrade the lock from LockWrite to LockRead.  Similarly, if an
2954    exclusive lock is obtained when only shared locks exist, then the
2955    cache manager will try to upgrade the lock from LockRead to
2956    LockWrite.
2957
2958    Each lock L owned by client C maintains a key L->key such that
2959    L->key == Key(C), the effective range defined by L->LOffset and
2960    L->LLength such that the range of bytes affected by the lock is
2961    (L->LOffset, +L->LLength), a type maintained in L->LockType which
2962    is either exclusive or shared.
2963
2964    Lock states:
2965
2966    A lock exists iff it is in S->fileLocks for some cm_scache_t
2967    S. Existing locks are in one of the following states: ACTIVE,
2968    WAITLOCK, WAITUNLOCK, LOST, DELETED.
2969
2970    The following sections describe each lock and the associated
2971    transitions.
2972
2973    1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
2974       the lock with the AFS file server.  This type of lock can be
2975       exercised by a client to read or write to the locked region (as
2976       the lock allows).
2977
2978       1.1 ACTIVE->LOST: When the AFS file server fails to extend a
2979         server lock that was required to assert the lock.  Before
2980         marking the lock as lost, the cache manager checks if the file
2981         has changed on the server.  If the file has not changed, then
2982         the cache manager will attempt to obtain a new server lock
2983         that is sufficient to assert the client side locks for the
2984         file.  If any of these fail, the lock is marked as LOST.
2985         Otherwise, it is left as ACTIVE.
2986
2987       1.2 ACTIVE->DELETED: Lock is released.
2988
2989    2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
2990       grants the lock but the lock is yet to be asserted with the AFS
2991       file server.  Once the file server grants the lock, the state
2992       will transition to an ACTIVE lock.
2993
2994       2.1 WAITLOCK->ACTIVE: The server granted the lock.
2995
2996       2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
2997         waiting.
2998
2999       2.3 WAITLOCK->LOST: One or more locks from this client were
3000         marked as LOST.  No further locks will be granted to this
3001         client until all lost locks are removed.
3002
3003    3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3004       receives a request for a lock that conflicts with an existing
3005       ACTIVE or WAITLOCK lock.  The lock will be placed in the queue
3006       and will be granted at such time the conflicting locks are
3007       removed, at which point the state will transition to either
3008       WAITLOCK or ACTIVE.
3009
3010       3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed.  The
3011         current serverLock is sufficient to assert this lock, or a
3012         sufficient serverLock is obtained.
3013
3014       3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3015         however the required serverLock is yet to be asserted with the
3016         server.
3017
3018       3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3019         released.
3020
3021       3.5 WAITUNLOCK->LOST: One or more locks from this client were
3022         marked as LOST.  No further locks will be granted to this
3023         client until all lost locks are removed.
3024
3025    4. LOST: A lock L is LOST if the server lock that was required to
3026       assert the lock could not be obtained or if it could not be
3027       extended, or if other locks by the same client were LOST.
3028       Essentially, once a lock is LOST, the contract between the cache
3029       manager and that specific client is no longer valid.
3030
3031       The cache manager rechecks the server lock once every minute and
3032       extends it as appropriate.  If this is not done for 5 minutes,
3033       the AFS file server will release the lock (the 5 minute timeout
3034       is based on current file server code and is fairly arbitrary).
3035       Once released, the lock cannot be re-obtained without verifying
3036       that the contents of the file hasn't been modified since the
3037       time the lock was released.  Re-obtaining the lock without
3038       verifying this may lead to data corruption.  If the lock can not
3039       be obtained safely, then all active locks for the cm_scache_t
3040       are marked as LOST.
3041
3042       4.1 LOST->DELETED: The lock is released.
3043
3044    5. DELETED: The lock is no longer relevant.  Eventually, it will
3045       get removed from the cm_scache_t. In the meantime, it will be
3046       treated as if it does not exist.
3047
3048       5.1 DELETED->not exist: The lock is removed from the
3049         cm_scache_t.
3050
3051    The following are classifications of locks based on their state.
3052
3053    6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK.  These locks
3054       have been accepted by the cache manager, but may or may not have
3055       been granted back to the client.
3056
3057    7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3058
3059    8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3060
3061    Lock operation:
3062
3063    A client C can READ range (Offset,+Length) of a file represented by
3064    cm_scache_t S iff (1):
3065
3066    1. for all _a_ in (Offset,+Length), all of the following is true:
3067
3068        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3069          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3070          shared.
3071
3072        1.2 For each LOST lock L in S->fileLocks such that _a_ in
3073          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3074          Key(C)
3075
3076        (When locks are lost on an cm_scache_t, all locks are lost.  By
3077        4.2 (below), if there is an exclusive LOST lock, then there
3078        can't be any overlapping ACTIVE locks.)
3079
3080    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3081
3082    2. for all _a_ in (Offset,+Length), one of the following is true:
3083
3084        2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3085          does not exist a LOST lock L such that _a_ in
3086          (L->LOffset,+L->LLength).
3087
3088        2.2 Byte _a_ of S is owned by C under lock L (as specified in
3089          1.2) AND L->LockType is exclusive.
3090
3091    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3092
3093    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3094       true:
3095
3096        3.1 If L->LockType is exclusive then there does NOT exist a
3097          ACCEPTED lock M in S->fileLocks such that _a_ in
3098          (M->LOffset,+M->LLength).
3099
3100          (If we count all QUEUED locks then we hit cases such as
3101          cascading waiting locks where the locks later on in the queue
3102          can be granted without compromising file integrity.  On the
3103          other hand if only ACCEPTED locks are considered, then locks
3104          that were received earlier may end up waiting for locks that
3105          were received later to be unlocked. The choice of ACCEPTED
3106          locks was made to mimic the Windows byte range lock
3107          semantics.)
3108
3109        3.2 If L->LockType is shared then for each ACCEPTED lock M in
3110          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3111          M->LockType is shared.
3112
3113    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3114
3115        4.1 M->key != Key(C)
3116
3117        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3118          and (M->LOffset,+M->LLength) do not intersect.
3119
3120          (Note: If a client loses a lock, it loses all locks.
3121          Subsequently, it will not be allowed to obtain any more locks
3122          until all existing LOST locks that belong to the client are
3123          released.  Once all locks are released by a single client,
3124          there exists no further contract between the client and AFS
3125          about the contents of the file, hence the client can then
3126          proceed to obtain new locks and establish a new contract.
3127
3128          This doesn't quite work as you think it should, because most
3129          applications aren't built to deal with losing locks they
3130          thought they once had.  For now, we don't have a good
3131          solution to lost locks.
3132
3133          Also, for consistency reasons, we have to hold off on
3134          granting locks that overlap exclusive LOST locks.)
3135
3136    A client C can only unlock locks L in S->fileLocks which have
3137    L->key == Key(C).
3138
3139    The representation and invariants are as follows:
3140
3141    - Each cm_scache_t structure keeps:
3142
3143        - A queue of byte-range locks (cm_scache_t::fileLocks) which
3144          are of type cm_file_lock_t.
3145
3146        - A record of the highest server-side lock that has been
3147          obtained for this object (cm_scache_t::serverLock), which is
3148          one of (-1), LockRead, LockWrite.
3149
3150        - A count of ACCEPTED exclusive and shared locks that are in the
3151          queue (cm_scache_t::sharedLocks and
3152          cm_scache_t::exclusiveLocks)
3153
3154    - Each cm_file_lock_t structure keeps:
3155
3156        - The type of lock (cm_file_lock_t::LockType)
3157
3158        - The key associated with the lock (cm_file_lock_t::key)
3159
3160        - The offset and length of the lock (cm_file_lock_t::LOffset
3161          and cm_file_lock_t::LLength)
3162
3163        - The state of the lock.
3164
3165        - Time of issuance or last successful extension
3166
3167    Semantic invariants:
3168
3169        I1. The number of ACCEPTED locks in S->fileLocks are
3170            (S->sharedLocks + S->exclusiveLocks)
3171
3172    External invariants:
3173
3174        I3. S->serverLock is the lock that we have asserted with the
3175            AFS file server for this cm_scache_t.
3176
3177        I4. S->serverLock == LockRead iff there is at least one ACTIVE
3178            shared lock, but no ACTIVE exclusive locks.
3179
3180        I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3181            exclusive lock.
3182
3183        I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3184            M->key == L->key IMPLIES M is LOST or DELETED.
3185
3186    --asanka
3187  */
3188
3189 #define IS_LOCK_ACTIVE(lockp)     (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3190
3191 #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)
3192
3193 #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)
3194
3195 #define IS_LOCK_LOST(lockp)       (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3196
3197 #define IS_LOCK_DELETED(lockp)    (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3198
3199 /* unsafe */
3200 #define IS_LOCK_ACCEPTED(lockp)   (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3201
3202 /* unsafe */
3203 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3204
3205 /* unsafe */
3206 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3207
3208 /* unsafe */
3209 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3210
3211 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3212 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3213 #else
3214 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3215 #endif
3216
3217 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3218
3219 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3220 {
3221     afs_int64 int_begin;
3222     afs_int64 int_end;
3223
3224     int_begin = MAX(pos->offset, neg->offset);
3225     int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3226
3227     if (int_begin < int_end) {
3228         if (int_begin == pos->offset) {
3229             pos->length = pos->offset + pos->length - int_end;
3230             pos->offset = int_end;
3231         } else if (int_end == pos->offset + pos->length) {
3232             pos->length = int_begin - pos->offset;
3233         }
3234
3235         /* We only subtract ranges if the resulting range is
3236            contiguous.  If we try to support non-contigous ranges, we
3237            aren't actually improving performance. */
3238     }
3239 }
3240
3241 /* Called with scp->mx held.  Returns 0 if all is clear to read the
3242    specified range by the client identified by key.
3243  */
3244 long cm_LockCheckRead(cm_scache_t *scp, 
3245                       LARGE_INTEGER LOffset, 
3246                       LARGE_INTEGER LLength, 
3247                       cm_key_t key)
3248 {
3249 #ifndef ADVISORY_LOCKS
3250
3251     cm_file_lock_t *fileLock;
3252     osi_queue_t *q;
3253     long code = 0;
3254     cm_range_t range;
3255     int substract_ranges = FALSE;
3256
3257     range.offset = LOffset.QuadPart;
3258     range.length = LLength.QuadPart;
3259
3260     /*
3261
3262      1. for all _a_ in (Offset,+Length), all of the following is true:
3263
3264        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3265          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3266          shared.
3267
3268        1.2 For each LOST lock L in S->fileLocks such that _a_ in
3269          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3270          Key(C)
3271
3272     */
3273
3274     lock_ObtainRead(&cm_scacheLock);
3275
3276     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3277         fileLock = 
3278             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3279
3280         if (INTERSECT_RANGE(range, fileLock->range)) {
3281             if (IS_LOCK_ACTIVE(fileLock)) {
3282                 if (fileLock->key == key) {
3283
3284                     /* If there is an active lock for this client, it
3285                        is safe to substract ranges.*/
3286                     cm_LockRangeSubtract(&range, &fileLock->range);
3287                     substract_ranges = TRUE;
3288                 } else {
3289                     if (fileLock->lockType != LockRead) {
3290                         code = CM_ERROR_LOCK_CONFLICT;
3291                         break;
3292                     }
3293
3294                     /* even if the entire range is locked for reading,
3295                        we still can't grant the lock at this point
3296                        because the client may have lost locks. That
3297                        is, unless we have already seen an active lock
3298                        belonging to the client, in which case there
3299                        can't be any lost locks for this client. */
3300                     if (substract_ranges)
3301                         cm_LockRangeSubtract(&range, &fileLock->range);
3302                 }
3303             } else if (IS_LOCK_LOST(fileLock) &&
3304                       (fileLock->key == key || fileLock->lockType == LockWrite)) {
3305                 code = CM_ERROR_BADFD;
3306                 break;
3307             }
3308         }
3309     }
3310
3311     lock_ReleaseRead(&cm_scacheLock);
3312
3313     osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3314               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3315
3316     return code;
3317
3318 #else
3319
3320     return 0;
3321
3322 #endif
3323 }
3324
3325 /* Called with scp->mx held.  Returns 0 if all is clear to write the
3326    specified range by the client identified by key.
3327  */
3328 long cm_LockCheckWrite(cm_scache_t *scp,
3329                        LARGE_INTEGER LOffset,
3330                        LARGE_INTEGER LLength,
3331                        cm_key_t key)
3332 {
3333 #ifndef ADVISORY_LOCKS
3334
3335     cm_file_lock_t *fileLock;
3336     osi_queue_t *q;
3337     long code = 0;
3338     cm_range_t range;
3339
3340     range.offset = LOffset.QuadPart;
3341     range.length = LLength.QuadPart;
3342
3343     /*
3344    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3345
3346    2. for all _a_ in (Offset,+Length), one of the following is true:
3347
3348        2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3349          lock L such that _a_ in (L->LOffset,+L->LLength).
3350
3351        2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3352          exclusive.
3353     */
3354
3355     lock_ObtainRead(&cm_scacheLock);
3356
3357     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3358         fileLock = 
3359             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3360
3361         if (INTERSECT_RANGE(range, fileLock->range)) {
3362             if (IS_LOCK_ACTIVE(fileLock)) {
3363                 if (fileLock->key == key) {
3364                     if (fileLock->lockType == LockWrite) {
3365
3366                         /* if there is an active lock for this client, it
3367                            is safe to substract ranges */
3368                         cm_LockRangeSubtract(&range, &fileLock->range);
3369                     } else {
3370                         code = CM_ERROR_LOCK_CONFLICT;
3371                         break;
3372                     }
3373                 } else {
3374                     code = CM_ERROR_LOCK_CONFLICT;
3375                     break;
3376                 }
3377             } else if (IS_LOCK_LOST(fileLock)) {
3378                 code = CM_ERROR_BADFD;
3379                 break;
3380             }
3381         }
3382     }
3383
3384     lock_ReleaseRead(&cm_scacheLock);
3385
3386     osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3387               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3388
3389     return code;
3390
3391 #else
3392
3393     return 0;
3394
3395 #endif
3396 }
3397
3398 /* Forward dcl. */
3399 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3400
3401 /* Called with cm_scacheLock write locked */
3402 static cm_file_lock_t * cm_GetFileLock(void) {
3403     cm_file_lock_t * l;
3404
3405     l = (cm_file_lock_t *) cm_freeFileLocks;
3406     if (l) {
3407         osi_QRemove(&cm_freeFileLocks, &l->q);
3408     } else {
3409         l = malloc(sizeof(cm_file_lock_t));
3410         osi_assert(l);
3411     }
3412
3413     memset(l, 0, sizeof(cm_file_lock_t));
3414
3415     return l;
3416 }
3417
3418 /* Called with cm_scacheLock write locked */
3419 static void cm_PutFileLock(cm_file_lock_t *l) {
3420     osi_QAdd(&cm_freeFileLocks, &l->q);
3421 }
3422
3423 /* called with scp->mx held.  May release it during processing, but
3424    leaves it held on exit. */
3425 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
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     osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
3440
3441     lock_ReleaseMutex(&scp->mx);
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_SetLock(callp, &tfid, lockType,
3450                              &volSync);
3451         rx_PutConnection(callp);
3452
3453     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3454                         NULL, NULL, code));
3455
3456     code = cm_MapRPCError(code, reqp);
3457     if (code) {
3458         osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3459     } else {
3460         osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3461     }
3462
3463     lock_ObtainMutex(&scp->mx);
3464
3465     return code;
3466 }
3467
3468 /* called with scp->mx held.  Releases it during processing */
3469 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
3470                        cm_req_t * reqp) {
3471     long code = 0;
3472     AFSFid tfid;