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