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