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