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