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