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