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