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