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