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