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