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