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