windows-afsd-rename-collision-20081226
[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     cm_scache_t *oldScp = NULL, *newScp = NULL;
3311
3312     if (cOldNamep == NULL || cNewNamep == NULL ||
3313         cm_ClientStrLen(cOldNamep) == 0 ||
3314         cm_ClientStrLen(cNewNamep) == 0)
3315         return CM_ERROR_INVAL;
3316
3317     /* 
3318      * Before we permit the operation, make sure that we do not already have
3319      * an object in the destination directory that has a case-insensitive match
3320      * for this name UNLESS the matching object is the object we are renaming.
3321      */
3322     code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3323     if (code) {
3324         osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed", 
3325                  oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3326         goto done;
3327     }
3328
3329     code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3330     if (code == 0) {
3331         /* found a matching object with the new name */
3332         if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3333             /* and they don't match so return an error */
3334             osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists", 
3335                       newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3336             code = CM_ERROR_EXISTS;
3337         }
3338         cm_ReleaseSCache(newScp);
3339         newScp = NULL;
3340     } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3341         code = CM_ERROR_EXISTS;
3342     } else {
3343         code = 0;
3344     }
3345     cm_ReleaseSCache(oldScp);
3346     oldScp = NULL;
3347
3348     if (code) 
3349         goto done;
3350
3351     if (oldNamep == NULL) {
3352         code = -1;
3353 #ifdef USE_BPLUS
3354         code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ, &oldDirOp);
3355         if (code == 0) {
3356             code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3357             if (code == 0)
3358                 free_oldNamep = TRUE;
3359             cm_EndDirOp(&oldDirOp);
3360         }
3361 #endif
3362         if (code) {
3363             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed", 
3364                       oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3365             goto done;
3366         }
3367     }
3368
3369
3370     /* before starting the RPC, mark that we're changing the directory data,
3371      * so that someone who does a chmod on the dir will wait until our call
3372      * completes.  We do this in vnode order so that we don't deadlock,
3373      * which makes the code a little verbose.
3374      */
3375     if (oldDscp == newDscp) {
3376         /* check for identical names */
3377         if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3378             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL", 
3379                       oldDscp, newDscp);
3380             code = CM_ERROR_RENAME_IDENTICAL;
3381             goto done;
3382         }
3383
3384         oneDir = 1;
3385         cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3386         lock_ObtainWrite(&oldDscp->rw);
3387         cm_dnlcRemove(oldDscp, cOldNamep);
3388         cm_dnlcRemove(oldDscp, cNewNamep);
3389         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3390                           CM_SCACHESYNC_STOREDATA);
3391         lock_ReleaseWrite(&oldDscp->rw);
3392         if (code != 0) {
3393             cm_EndDirOp(&oldDirOp);
3394         }
3395     }
3396     else {
3397         /* two distinct dir vnodes */
3398         oneDir = 0;
3399         if (oldDscp->fid.cell != newDscp->fid.cell ||
3400              oldDscp->fid.volume != newDscp->fid.volume) {
3401             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK", 
3402                       oldDscp, newDscp);
3403             code = CM_ERROR_CROSSDEVLINK;
3404             goto done;
3405         }
3406
3407         /* shouldn't happen that we have distinct vnodes for two
3408          * different files, but could due to deliberate attack, or
3409          * stale info.  Avoid deadlocks and quit now.
3410          */
3411         if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3412             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision", 
3413                       oldDscp, newDscp);
3414             code = CM_ERROR_CROSSDEVLINK;
3415             goto done;
3416         }
3417
3418         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3419             cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3420             lock_ObtainWrite(&oldDscp->rw);
3421             cm_dnlcRemove(oldDscp, cOldNamep);
3422             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3423                              CM_SCACHESYNC_STOREDATA);
3424             lock_ReleaseWrite(&oldDscp->rw);
3425             if (code != 0)
3426                 cm_EndDirOp(&oldDirOp);
3427             if (code == 0) {
3428                 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3429                 lock_ObtainWrite(&newDscp->rw);
3430                 cm_dnlcRemove(newDscp, cNewNamep);
3431                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3432                                  CM_SCACHESYNC_STOREDATA);
3433                 lock_ReleaseWrite(&newDscp->rw);
3434                 if (code) {
3435                     cm_EndDirOp(&newDirOp);
3436
3437                     /* cleanup first one */
3438                     lock_ObtainWrite(&oldDscp->rw);
3439                     cm_SyncOpDone(oldDscp, NULL,
3440                                    CM_SCACHESYNC_STOREDATA);
3441                     lock_ReleaseWrite(&oldDscp->rw);
3442                     cm_EndDirOp(&oldDirOp);
3443                 }       
3444             }
3445         }
3446         else {
3447             /* lock the new vnode entry first */
3448             cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3449             lock_ObtainWrite(&newDscp->rw);
3450             cm_dnlcRemove(newDscp, cNewNamep);
3451             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3452                               CM_SCACHESYNC_STOREDATA);
3453             lock_ReleaseWrite(&newDscp->rw);
3454             if (code != 0)
3455                 cm_EndDirOp(&newDirOp);
3456             if (code == 0) {
3457                 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3458                 lock_ObtainWrite(&oldDscp->rw);
3459                 cm_dnlcRemove(oldDscp, cOldNamep);
3460                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3461                                   CM_SCACHESYNC_STOREDATA);
3462                 lock_ReleaseWrite(&oldDscp->rw);
3463                 if (code != 0)
3464                     cm_EndDirOp(&