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