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