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