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