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