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