Windows: ignore SYNCHRONIZE privilege in cm_CheckNTOpen
[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)
4043          and (M->LOffset,+M->LLength) do not intersect.
4044
4045          (Note: If a client loses a lock, it loses all locks.
4046          Subsequently, it will not be allowed to obtain any more locks
4047          until all existing LOST locks that belong to the client are
4048          released.  Once all locks are released by a single client,
4049          there exists no further contract between the client and AFS
4050          about the contents of the file, hence the client can then
4051          proceed to obtain new locks and establish a new contract.
4052
4053          This doesn't quite work as you think it should, because most
4054          applications aren't built to deal with losing locks they
4055          thought they once had.  For now, we don't have a good
4056          solution to lost locks.
4057
4058          Also, for consistency reasons, we have to hold off on
4059          granting locks that overlap exclusive LOST locks.)
4060
4061    A client C can only unlock locks L in S->fileLocks which have
4062    L->key == Key(C).
4063
4064    The representation and invariants are as follows:
4065
4066    - Each cm_scache_t structure keeps:
4067
4068        - A queue of byte-range locks (cm_scache_t::fileLocks) which
4069          are of type cm_file_lock_t.
4070
4071        - A record of the highest server-side lock that has been
4072          obtained for this object (cm_scache_t::serverLock), which is
4073          one of (-1), LockRead, LockWrite.
4074
4075        - A count of ACCEPTED exclusive and shared locks that are in the
4076          queue (cm_scache_t::sharedLocks and
4077          cm_scache_t::exclusiveLocks)
4078
4079    - Each cm_file_lock_t structure keeps:
4080
4081        - The type of lock (cm_file_lock_t::LockType)
4082
4083        - The key associated with the lock (cm_file_lock_t::key)
4084
4085        - The offset and length of the lock (cm_file_lock_t::LOffset
4086          and cm_file_lock_t::LLength)
4087
4088        - The state of the lock.
4089
4090        - Time of issuance or last successful extension
4091
4092    Semantic invariants:
4093
4094        I1. The number of ACCEPTED locks in S->fileLocks are
4095            (S->sharedLocks + S->exclusiveLocks)
4096
4097    External invariants:
4098
4099        I3. S->serverLock is the lock that we have asserted with the
4100            AFS file server for this cm_scache_t.
4101
4102        I4. S->serverLock == LockRead iff there is at least one ACTIVE
4103            shared lock, but no ACTIVE exclusive locks.
4104
4105        I5. S->serverLock == LockWrite iff there is at least one ACTIVE
4106            exclusive lock.
4107
4108        I6. If L is a LOST lock, then for each lock M in S->fileLocks,
4109            M->key == L->key IMPLIES M is LOST or DELETED.
4110
4111    --asanka
4112  */
4113
4114 #define IS_LOCK_ACTIVE(lockp)     (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
4115
4116 #define IS_LOCK_WAITLOCK(lockp)   (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_WAITLOCK)
4117
4118 #define IS_LOCK_WAITUNLOCK(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_WAITUNLOCK)
4119
4120 #define IS_LOCK_LOST(lockp)       (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
4121
4122 #define IS_LOCK_DELETED(lockp)    (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
4123
4124 /* unsafe */
4125 #define IS_LOCK_ACCEPTED(lockp)   (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
4126
4127 /* unsafe */
4128 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
4129
4130 /* unsafe */
4131 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
4132
4133 /* unsafe */
4134 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
4135
4136 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
4137 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
4138 #else
4139 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
4140 #endif
4141
4142 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
4143
4144 #if defined(VICED_CAPABILITY_WRITELOCKACL)
4145 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
4146 #else
4147 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
4148
4149 /* This should really be defined in any build that this code is being
4150    compiled. */
4151 #error  VICED_CAPABILITY_WRITELOCKACL not defined.
4152 #endif
4153
4154 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
4155 {
4156     afs_int64 int_begin;
4157     afs_int64 int_end;
4158
4159     int_begin = MAX(pos->offset, neg->offset);
4160     int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
4161
4162     if (int_begin < int_end) {
4163         if (int_begin == pos->offset) {
4164             pos->length = pos->offset + pos->length - int_end;
4165             pos->offset = int_end;
4166         } else if (int_end == pos->offset + pos->length) {
4167             pos->length = int_begin - pos->offset;
4168         }
4169
4170         /* We only subtract ranges if the resulting range is
4171            contiguous.  If we try to support non-contigous ranges, we
4172            aren't actually improving performance. */
4173     }
4174 }
4175
4176 /* Called with scp->rw held.  Returns 0 if all is clear to read the
4177    specified range by the client identified by key.
4178  */
4179 long cm_LockCheckRead(cm_scache_t *scp,
4180                       LARGE_INTEGER LOffset,
4181                       LARGE_INTEGER LLength,
4182                       cm_key_t key)
4183 {
4184 #ifndef ADVISORY_LOCKS
4185
4186     cm_file_lock_t *fileLock;
4187     osi_queue_t *q;
4188     long code = 0;
4189     cm_range_t range;
4190     int substract_ranges = FALSE;
4191
4192     range.offset = LOffset.QuadPart;
4193     range.length = LLength.QuadPart;
4194
4195     /*
4196
4197      1. for all _a_ in (Offset,+Length), all of the following is true:
4198
4199        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4200          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4201          shared.
4202
4203        1.2 For each LOST lock L in S->fileLocks such that _a_ in
4204          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4205          Key(C)
4206
4207     */
4208
4209     lock_ObtainRead(&cm_scacheLock);
4210
4211     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4212         fileLock =
4213             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4214
4215         if (INTERSECT_RANGE(range, fileLock->range)) {
4216             if (IS_LOCK_ACTIVE(fileLock)) {
4217                 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4218
4219                     /* If there is an active lock for this client, it
4220                        is safe to substract ranges.*/
4221                     cm_LockRangeSubtract(&range, &fileLock->range);
4222                     substract_ranges = TRUE;
4223                 } else {
4224                     if (fileLock->lockType != LockRead) {
4225                         code = CM_ERROR_LOCK_CONFLICT;
4226                         break;
4227                     }
4228
4229                     /* even if the entire range is locked for reading,
4230                        we still can't grant the lock at this point
4231                        because the client may have lost locks. That
4232                        is, unless we have already seen an active lock
4233                        belonging to the client, in which case there
4234                        can't be any lost locks for this client. */
4235                     if (substract_ranges)
4236                         cm_LockRangeSubtract(&range, &fileLock->range);
4237                 }
4238             } else if (IS_LOCK_LOST(fileLock) &&
4239                        (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4240                 code = CM_ERROR_BADFD;
4241                 break;
4242             }
4243         }
4244     }
4245
4246     lock_ReleaseRead(&cm_scacheLock);
4247
4248     osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4249               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4250
4251     return code;
4252
4253 #else
4254
4255     return 0;
4256
4257 #endif
4258 }
4259
4260 /* Called with scp->rw held.  Returns 0 if all is clear to write the
4261    specified range by the client identified by key.
4262  */
4263 long cm_LockCheckWrite(cm_scache_t *scp,
4264                        LARGE_INTEGER LOffset,
4265                        LARGE_INTEGER LLength,
4266                        cm_key_t key)
4267 {
4268 #ifndef ADVISORY_LOCKS
4269
4270     cm_file_lock_t *fileLock;
4271     osi_queue_t *q;
4272     long code = 0;
4273     cm_range_t range;
4274
4275     range.offset = LOffset.QuadPart;
4276     range.length = LLength.QuadPart;
4277
4278     /*
4279    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4280
4281    2. for all _a_ in (Offset,+Length), one of the following is true:
4282
4283        2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4284          lock L such that _a_ in (L->LOffset,+L->LLength).
4285
4286        2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4287          exclusive.
4288     */
4289
4290     lock_ObtainRead(&cm_scacheLock);
4291
4292     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4293         fileLock =
4294             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4295
4296         if (INTERSECT_RANGE(range, fileLock->range)) {
4297             if (IS_LOCK_ACTIVE(fileLock)) {
4298                 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4299                     if (fileLock->lockType == LockWrite) {
4300
4301                         /* if there is an active lock for this client, it
4302                            is safe to substract ranges */
4303                         cm_LockRangeSubtract(&range, &fileLock->range);
4304                     } else {
4305                         code = CM_ERROR_LOCK_CONFLICT;
4306                         break;
4307                     }
4308                 } else {
4309                     code = CM_ERROR_LOCK_CONFLICT;
4310                     break;
4311                 }
4312             } else if (IS_LOCK_LOST(fileLock)) {
4313                 code = CM_ERROR_BADFD;
4314                 break;
4315             }
4316         }
4317     }
4318
4319     lock_ReleaseRead(&cm_scacheLock);
4320
4321     osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4322               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4323
4324     return code;
4325
4326 #else
4327
4328     return 0;
4329
4330 #endif
4331 }
4332
4333 /* Called with cm_scacheLock write locked */
4334 static cm_file_lock_t * cm_GetFileLock(void) {
4335     cm_file_lock_t * l;
4336
4337     l = (cm_file_lock_t *) cm_freeFileLocks;
4338     if (l) {
4339         osi_QRemove(&cm_freeFileLocks, &l->q);
4340     } else {
4341         l = malloc(sizeof(cm_file_lock_t));
4342         osi_assertx(l, "null cm_file_lock_t");
4343     }
4344
4345     memset(l, 0, sizeof(cm_file_lock_t));
4346
4347     return l;
4348 }
4349
4350 /* Called with cm_scacheLock write locked */
4351 static void cm_PutFileLock(cm_file_lock_t *l) {
4352     osi_QAdd(&cm_freeFileLocks, &l->q);
4353 }
4354
4355 /* called with scp->rw held.  May release it during processing, but
4356    leaves it held on exit. */
4357 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4358                    cm_req_t * reqp) {
4359     long code = 0;
4360     AFSFid tfid;
4361     cm_fid_t cfid;
4362     cm_conn_t * connp;
4363     struct rx_connection * rxconnp;
4364     AFSVolSync volSync;
4365     afs_uint32 reqflags = reqp->flags;
4366
4367     memset(&volSync, 0, sizeof(volSync));
4368
4369     tfid.Volume = scp->fid.volume;
4370     tfid.Vnode = scp->fid.vnode;
4371     tfid.Unique = scp->fid.unique;
4372     cfid = scp->fid;
4373
4374     osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4375
4376     reqp->flags |= CM_REQ_NORETRY;
4377     lock_ReleaseWrite(&scp->rw);
4378
4379     do {
4380         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4381         if (code)
4382             break;
4383
4384         rxconnp = cm_GetRxConn(connp);
4385         code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4386                              &volSync);
4387         rx_PutConnection(rxconnp);
4388
4389     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4390                         NULL, NULL, code));
4391
4392     code = cm_MapRPCError(code, reqp);
4393     if (code) {
4394         osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4395     } else {
4396         osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4397     }
4398
4399     reqp->flags = reqflags;
4400
4401     lock_ObtainWrite(&scp->rw);
4402     if (code == 0) {
4403         /*
4404          * The file server does not return a status structure so we must
4405          * locally track the file server lock count to the best of our
4406          * ability.
4407          */
4408         if (lockType == LockWrite)
4409             scp->fsLockCount = -1;
4410         else
4411             scp->fsLockCount++;
4412     }
4413     return code;
4414 }
4415
4416 /* called with scp->rw held.  Releases it during processing */
4417 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4418                        cm_req_t * reqp) {
4419     long code = 0;
4420     AFSFid tfid;
4421     cm_fid_t cfid;
4422     cm_conn_t * connp;
4423     struct rx_connection * rxconnp;
4424     AFSVolSync volSync;
4425
4426     if (scp->flags & CM_SCACHEFLAG_DELETED) {
4427         osi_Log1(afsd_logp, "CALL ReleaseLock on Deleted Vnode scp 0x%p", scp);
4428         return 0;
4429     }
4430
4431     memset(&volSync, 0, sizeof(volSync));
4432
4433     tfid.Volume = scp->fid.volume;
4434     tfid.Vnode = scp->fid.vnode;
4435     tfid.Unique = scp->fid.unique;
4436     cfid = scp->fid;
4437
4438     lock_ReleaseWrite(&scp->rw);
4439
4440     osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4441
4442     do {
4443         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4444         if (code)
4445             break;
4446
4447         rxconnp = cm_GetRxConn(connp);
4448         code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4449         rx_PutConnection(rxconnp);
4450
4451     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4452                         NULL, NULL, code));
4453     code = cm_MapRPCError(code, reqp);
4454     if (code)
4455         osi_Log1(afsd_logp,
4456                  "CALL ReleaseLock FAILURE, code 0x%x", code);
4457     else
4458         osi_Log0(afsd_logp,
4459                  "CALL ReleaseLock SUCCESS");
4460
4461     lock_ObtainWrite(&scp->rw);
4462     if (code == 0) {
4463         /*
4464          * The file server does not return a status structure so we must
4465          * locally track the file server lock count to the best of our
4466          * ability.
4467          */
4468         scp->fsLockCount--;
4469         if (scp->fsLockCount < 0)
4470             scp->fsLockCount = 0;
4471     }
4472
4473     return (code != CM_ERROR_BADFD ? code : 0);
4474 }
4475
4476 /* called with scp->rw held.  May release it during processing, but
4477    will exit with lock held.
4478
4479    This will return:
4480
4481    - 0 if the user has permission to get the specified lock for the scp
4482
4483    - CM_ERROR_NOACCESS if not
4484
4485    Any other error from cm_SyncOp will be sent down untranslated.
4486
4487    If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4488    phas_insert (if non-NULL) will receive a boolean value indicating
4489    whether the user has INSERT permission or not.
4490 */
4491 long cm_LockCheckPerms(cm_scache_t * scp,
4492                        int lock_type,
4493                        cm_user_t * userp,
4494                        cm_req_t * reqp,
4495                        int * phas_insert)
4496 {
4497     long rights = 0;
4498     long code = 0, code2 = 0;
4499
4500     /* lock permissions are slightly tricky because of the 'i' bit.
4501        If the user has PRSFS_LOCK, she can read-lock the file.  If the
4502        user has PRSFS_WRITE, she can write-lock the file.  However, if
4503        the user has PRSFS_INSERT, then she can write-lock new files,
4504        but not old ones.  Since we don't have information about
4505        whether a file is new or not, we assume that if the user owns
4506        the scp, then she has the permissions that are granted by
4507        PRSFS_INSERT. */
4508
4509     osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4510              scp, lock_type, userp);
4511
4512     if (lock_type == LockRead)
4513         rights |= PRSFS_LOCK;
4514     else if (lock_type == LockWrite)
4515         rights |= PRSFS_WRITE | PRSFS_LOCK;
4516     else {
4517         /* hmmkay */
4518         osi_assertx(FALSE, "invalid lock type");
4519         return 0;
4520     }
4521
4522     if (phas_insert)
4523         *phas_insert = FALSE;
4524
4525     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4526                      CM_SCACHESYNC_GETSTATUS |
4527                      CM_SCACHESYNC_NEEDCALLBACK);
4528
4529     if (phas_insert && scp->creator == userp) {
4530
4531         /* If this file was created by the user, then we check for
4532            PRSFS_INSERT.  If the file server is recent enough, then
4533            this should be sufficient for her to get a write-lock (but
4534            not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4535            indicates whether a file server supports getting write
4536            locks when the user only has PRSFS_INSERT.
4537
4538            If the file was not created by the user we skip the check
4539            because the INSERT bit will not apply to this user even
4540            if it is set.
4541          */
4542
4543         code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4544                          CM_SCACHESYNC_GETSTATUS |
4545                          CM_SCACHESYNC_NEEDCALLBACK);
4546
4547         if (code2 == CM_ERROR_NOACCESS) {
4548             osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4549         } else {
4550             *phas_insert = TRUE;
4551             osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4552         }
4553     }
4554
4555     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4556
4557     osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4558
4559     return code;
4560 }
4561
4562 /* called with scp->rw held */
4563 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4564              LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4565              cm_key_t key,
4566              int allowWait, cm_user_t *userp, cm_req_t *reqp,
4567              cm_file_lock_t **lockpp)
4568 {
4569     long code = 0;
4570     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4571     cm_file_lock_t *fileLock;
4572     osi_queue_t *q;
4573     cm_range_t range;
4574     int wait_unlock = FALSE;
4575     int force_client_lock = FALSE;
4576
4577     osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4578              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4579     osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4580              key.process_id, key.session_id, key.file_id);
4581
4582     /*
4583    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4584
4585    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4586       true:
4587
4588        3.1 If L->LockType is exclusive then there does NOT exist a
4589          ACCEPTED lock M in S->fileLocks such that _a_ in
4590          (M->LOffset,+M->LLength).
4591
4592        3.2 If L->LockType is shared then for each ACCEPTED lock M in
4593          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4594          M->LockType is shared.
4595
4596    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4597
4598        4.1 M->key != Key(C)
4599
4600        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4601          and (M->LOffset,+M->LLength) do not intersect.
4602     */
4603
4604     range.offset = LOffset.QuadPart;
4605     range.length = LLength.QuadPart;
4606
4607     lock_ObtainRead(&cm_scacheLock);
4608
4609     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4610         fileLock =
4611             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4612
4613         if (IS_LOCK_LOST(fileLock)) {
4614             if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4615                 code = CM_ERROR_BADFD;
4616                 break;
4617             } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4618                 code = CM_ERROR_WOULDBLOCK;
4619                 wait_unlock = TRUE;
4620                 break;
4621             }
4622         }
4623
4624         /* we don't need to check for deleted locks here since deleted
4625            locks are dequeued from scp->fileLocks */
4626         if (IS_LOCK_ACCEPTED(fileLock) &&
4627            INTERSECT_RANGE(range, fileLock->range)) {
4628
4629             if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4630                 fileLock->lockType != LockRead) {
4631                 wait_unlock = TRUE;
4632                 code = CM_ERROR_WOULDBLOCK;
4633                 break;
4634             }
4635         }
4636     }
4637
4638     lock_ReleaseRead(&cm_scacheLock);
4639
4640     if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4641         if (Which == scp->serverLock ||
4642            (Which == LockRead && scp->serverLock == LockWrite)) {
4643
4644             int has_insert = 0;
4645
4646             /* we already have the lock we need */
4647             osi_Log3(afsd_logp, "   we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4648                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4649
4650             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4651
4652             /* special case: if we don't have permission to read-lock
4653                the file, then we force a clientside lock.  This is to
4654                compensate for applications that obtain a read-lock for
4655                reading files off of directories that don't grant
4656                read-locks to the user. */
4657             if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4658
4659                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4660                     osi_Log0(afsd_logp, "   User has no read-lock perms, but has INSERT perms.");
4661                     code = 0;
4662                 } else {
4663                     osi_Log0(afsd_logp, "   User has no read-lock perms. Forcing client-side lock");
4664                     force_client_lock = TRUE;
4665                 }
4666             }
4667
4668         } else if ((scp->exclusiveLocks > 0) ||
4669                    (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4670             int has_insert = 0;
4671
4672             /* We are already waiting for some other lock.  We should
4673                wait for the daemon to catch up instead of generating a
4674                flood of SetLock calls. */
4675             osi_Log3(afsd_logp, "   already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4676                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4677
4678             /* see if we have permission to create the lock in the
4679                first place. */
4680             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4681             if (code == 0)
4682                 code = CM_ERROR_WOULDBLOCK;
4683             else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4684
4685                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4686                     osi_Log0(afsd_logp,
4687                              "   User has no read-lock perms, but has INSERT perms.");
4688                     code = CM_ERROR_WOULDBLOCK;
4689                 } else {
4690                     osi_Log0(afsd_logp,
4691                              "   User has no read-lock perms. Forcing client-side lock");
4692                     force_client_lock = TRUE;
4693                 }
4694             }
4695
4696             /* leave any other codes as-is */
4697
4698         } else {
4699             int newLock;
4700             int check_data_version = FALSE;
4701             int has_insert = 0;
4702
4703             /* first check if we have permission to elevate or obtain
4704                the lock. */
4705             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4706             if (code) {
4707                 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4708                     (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4709                     osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
4710                     force_client_lock = TRUE;
4711                 }
4712                 goto check_code;
4713             }
4714
4715             /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4716
4717             if (scp->serverLock == LockRead && Which == LockWrite) {
4718
4719                 /* We want to escalate the lock to a LockWrite.
4720                  * Unfortunately that's not really possible without
4721                  * letting go of the current lock.  But for now we do
4722                  * it anyway. */
4723
4724                 osi_Log0(afsd_logp,
4725                          "   attempting to UPGRADE from LockRead to LockWrite.");
4726                 osi_Log1(afsd_logp,
4727                          "   dataVersion on scp: %I64d", scp->dataVersion);
4728
4729                 /* we assume at this point (because scp->serverLock
4730                    was valid) that we had a valid server lock. */
4731                 scp->lockDataVersion = scp->dataVersion;
4732                 check_data_version = TRUE;
4733
4734                 code = cm_IntReleaseLock(scp, userp, reqp);
4735
4736                 if (code) {
4737                     /* We couldn't release the lock */
4738                     goto check_code;
4739                 } else {
4740                     scp->serverLock = -1;
4741                 }
4742             }
4743
4744             /* We need to obtain a server lock of type Which in order
4745              * to assert this file lock */
4746 #ifndef AGGRESSIVE_LOCKS
4747             newLock = Which;
4748 #else
4749             newLock = LockWrite;
4750 #endif
4751
4752             code = cm_IntSetLock(scp, userp, newLock, reqp);
4753
4754 #ifdef AGGRESSIVE_LOCKS
4755             if ((code == CM_ERROR_WOULDBLOCK ||
4756                  code == CM_ERROR_NOACCESS) && newLock != Which) {
4757                 /* we wanted LockRead.  We tried LockWrite. Now try
4758                  * LockRead again */
4759                 newLock = Which;
4760
4761                 /* am I sane? */
4762                 osi_assertx(newLock == LockRead, "lock type not read");
4763
4764                 code = cm_IntSetLock(scp, userp, newLock, reqp);
4765             }
4766 #endif
4767
4768             if (code == CM_ERROR_NOACCESS) {
4769                 if (Which == LockRead) {
4770                     if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4771                         long tcode;
4772                         /* We requested a read-lock, but we have permission to
4773                          * get a write-lock. Try that */
4774
4775                         tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4776
4777                         if (tcode == 0) {
4778                             newLock = LockWrite;
4779
4780                             osi_Log0(afsd_logp, "   User has 'i' perms and the request was for a LockRead.  Trying to get a LockWrite instead");
4781
4782                             code = cm_IntSetLock(scp, userp, newLock, reqp);
4783                         }
4784                     } else {
4785                         osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
4786                         force_client_lock = TRUE;
4787                     }
4788                 } else if (Which == LockWrite &&
4789                            scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4790                     long tcode;
4791
4792                     /* Special case: if the lock request was for a
4793                      * LockWrite and the user owns the file and we weren't
4794                      * allowed to obtain the serverlock, we either lost a
4795                      * race (the permissions changed from under us), or we
4796                      * have 'i' bits, but we aren't allowed to lock the
4797                      * file. */
4798
4799                     /* check if we lost a race... */
4800                     tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4801
4802                     if (tcode == 0) {
4803                         osi_Log0(afsd_logp, "   User has 'i' perms but can't obtain write locks. Using client-side locks.");
4804                         force_client_lock = TRUE;
4805                     }
4806                 }
4807             }
4808
4809             if (code == 0 && check_data_version &&
4810                scp->dataVersion != scp->lockDataVersion) {
4811                 /* We lost a race.  Although we successfully obtained
4812                  * a lock, someone modified the file in between.  The
4813                  * locks have all been technically lost. */
4814
4815                 osi_Log0(afsd_logp,
4816                          "  Data version mismatch while upgrading lock.");
4817                 osi_Log2(afsd_logp,
4818                          "  Data versions before=%I64d, after=%I64d",
4819                          scp->lockDataVersion,
4820                          scp->dataVersion);
4821                 osi_Log1(afsd_logp,
4822                          "  Releasing stale lock for scp 0x%x", scp);
4823
4824                 code = cm_IntReleaseLock(scp, userp, reqp);
4825
4826                 scp->serverLock = -1;
4827
4828                 code = CM_ERROR_INVAL;
4829             } else if (code == 0) {
4830                 scp->serverLock = newLock;
4831                 scp->lockDataVersion = scp->dataVersion;
4832             }
4833
4834             if (code != 0 &&
4835                 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4836                 scp->serverLock == -1) {
4837                 /* Oops. We lost the lock. */
4838                 cm_LockMarkSCacheLost(scp);
4839             }
4840         }
4841     } else if (code == 0) {     /* server locks not enabled */
4842         osi_Log0(afsd_logp,
4843                  "  Skipping server lock for scp");
4844     }
4845
4846  check_code:
4847
4848     if (code != 0 && !force_client_lock) {
4849         /* Special case error translations
4850
4851            Applications don't expect certain errors from a
4852            LockFile/UnlockFile call.  We need to translate some error
4853            code to codes that apps expect and handle. */
4854
4855         /* We shouldn't actually need to handle this case since we
4856            simulate locks for RO scps anyway. */
4857         if (code == CM_ERROR_READONLY) {
4858             osi_Log0(afsd_logp, "   Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4859             code = CM_ERROR_NOACCESS;
4860         }
4861     }
4862
4863     if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4864         force_client_lock) {
4865
4866         /* clear the error if we are forcing a client lock, so we
4867            don't get confused later. */
4868         if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4869             code = 0;
4870
4871         cm_HoldUser(userp);
4872
4873         lock_ObtainWrite(&cm_scacheLock);
4874         fileLock = cm_GetFileLock();
4875 #ifdef DEBUG
4876         fileLock->fid = scp->fid;
4877 #endif
4878         fileLock->key = key;
4879         fileLock->lockType = Which;
4880         fileLock->userp = userp;
4881         fileLock->range = range;
4882         fileLock->flags = (code == 0 ? 0 :
4883                            ((wait_unlock)?
4884                             CM_FILELOCK_FLAG_WAITUNLOCK :
4885                             CM_FILELOCK_FLAG_WAITLOCK));
4886
4887         if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4888             fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4889
4890         fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4891
4892         osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4893         cm_HoldSCacheNoLock(scp);
4894         fileLock->scp = scp;
4895         osi_QAdd(&cm_allFileLocks, &fileLock->q);
4896         lock_ReleaseWrite(&cm_scacheLock);
4897
4898         if (code != 0) {
4899             *lockpp = fileLock;
4900         }
4901
4902         if (IS_LOCK_CLIENTONLY(fileLock)) {
4903             scp->clientLocks++;
4904         } else if (IS_LOCK_ACCEPTED(fileLock)) {
4905             if (Which == LockRead)
4906                 scp->sharedLocks++;
4907             else
4908                 scp->exclusiveLocks++;
4909         }
4910
4911         osi_Log3(afsd_logp,
4912                  "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4913                  fileLock, fileLock->flags, scp);
4914         osi_Log4(afsd_logp,
4915                  "   exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4916                  scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4917                  (int)(signed char) scp->serverLock);
4918     } else {
4919         osi_Log1(afsd_logp,
4920                  "cm_Lock Rejecting lock (code = 0x%x)", code);
4921     }
4922
4923     /* Convert from would block to lock not granted */
4924     if (code == CM_ERROR_WOULDBLOCK)
4925         code = CM_ERROR_LOCK_NOT_GRANTED;
4926
4927     return code;
4928 }
4929
4930 static long
4931 cm_IntUnlock(cm_scache_t * scp,
4932              cm_user_t * userp,
4933              cm_req_t *  reqp)
4934 {
4935     long code = 0;
4936
4937     osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4938     osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4939     osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4940
4941     if (!SERVERLOCKS_ENABLED(scp)) {
4942         osi_Log0(afsd_logp, "  Skipping server lock for scp");
4943         goto done;
4944     }
4945
4946     /* Ideally we would go through the rest of the locks to determine
4947      * if one or more locks that were formerly in WAITUNLOCK can now
4948      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4949      * scp->sharedLocks accordingly.  However, the retrying of locks
4950      * in that manner is done cm_RetryLock() manually.
4951      */
4952
4953     if (scp->serverLock == LockWrite &&
4954         scp->exclusiveLocks == 0 &&
4955         scp->sharedLocks > 0) {
4956         /* The serverLock should be downgraded to LockRead */
4957         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
4958
4959         /* Make sure there are no dirty buffers left. */
4960         code = cm_FSync(scp, userp, reqp, TRUE);
4961
4962         /* since scp->serverLock looked sane, we are going to assume
4963            that we have a valid server lock. */
4964         scp->lockDataVersion = scp->dataVersion;
4965         osi_Log1(afsd_logp, "  dataVersion on scp = %I64d", scp->dataVersion);
4966
4967         /* before we downgrade, make sure that we have enough
4968            permissions to get the read lock. */
4969         code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
4970         if (code != 0) {
4971
4972             osi_Log0(afsd_logp, "  SKIPPING downgrade because user doesn't have perms to get downgraded lock");
4973
4974             code = 0;
4975             goto done;
4976         }
4977
4978         code = cm_IntReleaseLock(scp, userp, reqp);
4979
4980         if (code) {
4981             /* so we couldn't release it.  Just let the lock be for now */
4982             code = 0;
4983             goto done;
4984         } else {
4985             scp->serverLock = -1;
4986         }
4987
4988         code = cm_IntSetLock(scp, userp, LockRead, reqp);
4989
4990         if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4991             scp->serverLock = LockRead;
4992         } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4993             /* We lost a race condition.  Although we have a valid
4994                lock on the file, the data has changed and essentially
4995                we have lost the lock we had during the transition. */
4996
4997             osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4998             osi_Log2(afsd_logp, "  Data versions before=%I64d, after=%I64d",
4999                      scp->lockDataVersion,
5000                      scp->dataVersion);
5001
5002             code = cm_IntReleaseLock(scp, userp, reqp);
5003
5004             code = CM_ERROR_INVAL;
5005             scp->serverLock = -1;
5006         }
5007
5008         if (code != 0 &&
5009             (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5010                 (scp->serverLock == -1)) {
5011                 /* Oopsie */
5012                 cm_LockMarkSCacheLost(scp);
5013             }
5014
5015         /* failure here has no bearing on the return value of cm_Unlock() */
5016         code = 0;
5017
5018     } else if (scp->serverLock != (-1) &&
5019               scp->exclusiveLocks == 0 &&
5020               scp->sharedLocks == 0) {
5021         /* The serverLock should be released entirely */
5022
5023         if (scp->serverLock == LockWrite) {
5024             osi_Log0(afsd_logp, "  RELEASE LockWrite -> LockNone");
5025
5026             /* Make sure there are no dirty buffers left. */
5027             code = cm_FSync(scp, userp, reqp, TRUE);
5028         } else {
5029             osi_Log0(afsd_logp, "  RELEASE LockRead -> LockNone");
5030         }
5031
5032         code = cm_IntReleaseLock(scp, userp, reqp);
5033
5034         if (code == 0)
5035             scp->serverLock = (-1);
5036     }
5037
5038   done:
5039     return code;
5040 }
5041 /* Called with scp->rw held */
5042 long cm_UnlockByKey(cm_scache_t * scp,
5043                     cm_key_t key,
5044                     afs_uint32 flags,
5045                     cm_user_t * userp,
5046                     cm_req_t * reqp)
5047 {
5048     long code = 0;
5049     cm_file_lock_t *fileLock;
5050     osi_queue_t *q, *qn;
5051     int n_unlocks = 0;
5052
5053     osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
5054              scp, key.process_id, key.session_id, key.file_id);
5055     osi_Log1(afsd_logp, "    flags=0x%x", flags);
5056
5057     lock_ObtainWrite(&cm_scacheLock);
5058
5059     for (q = scp->fileLocksH; q; q = qn) {
5060         qn = osi_QNext(q);
5061
5062         fileLock = (cm_file_lock_t *)
5063             ((char *) q - offsetof(cm_file_lock_t, fileq));
5064
5065 #ifdef DEBUG
5066         osi_Log4(afsd_logp, "   Checking lock[0x%x] range[%d,+%d] type[%d]",
5067                  fileLock,
5068                  (unsigned long) fileLock->range.offset,
5069                  (unsigned long) fileLock->range.length,
5070                 fileLock->lockType);
5071         osi_Log4(afsd_logp, "     key<0x%x, 0x%x, 0x%x> flags[0x%x]",
5072                  fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
5073                  fileLock->flags);
5074
5075         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5076             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5077             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5078                      fileLock->fid.cell,
5079                      fileLock->fid.volume,
5080                      fileLock->fid.vnode,
5081                      fileLock->fid.unique);
5082             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5083                      fileLock->scp->fid.cell,
5084                      fileLock->scp->fid.volume,
5085                      fileLock->scp->fid.vnode,
5086                      fileLock->scp->fid.unique);
5087             osi_assertx(FALSE, "invalid fid value");
5088         }
5089 #endif
5090
5091         if (!IS_LOCK_DELETED(fileLock) &&
5092             cm_KeyEquals(&fileLock->key, &key, flags)) {
5093             osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
5094                     fileLock->range.offset,
5095                     fileLock->range.length,
5096                     fileLock->lockType);
5097
5098             osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5099
5100             if (IS_LOCK_CLIENTONLY(fileLock)) {
5101                 scp->clientLocks--;
5102             } else if (IS_LOCK_ACCEPTED(fileLock)) {
5103                 if (fileLock->lockType == LockRead)
5104                     scp->sharedLocks--;
5105                 else
5106                     scp->exclusiveLocks--;
5107             }
5108
5109             fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5110
5111             n_unlocks++;
5112         }
5113     }
5114
5115     lock_ReleaseWrite(&cm_scacheLock);
5116
5117     if (n_unlocks == 0) {
5118         osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
5119         osi_Log3(afsd_logp, "   Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
5120                  scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
5121
5122         return 0;
5123     }
5124
5125     osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
5126
5127     osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
5128     osi_Log4(afsd_logp, "   Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5129              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5130              (int)(signed char) scp->serverLock);
5131
5132     return code;
5133 }
5134
5135 long cm_Unlock(cm_scache_t *scp,
5136                unsigned char sLockType,
5137                LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
5138                cm_key_t key,
5139                afs_uint32 flags,
5140                cm_user_t *userp,
5141                cm_req_t *reqp)
5142 {
5143     long code = 0;
5144     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
5145     cm_file_lock_t *fileLock;
5146     osi_queue_t *q;
5147     int release_userp = FALSE;
5148     int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
5149     int lock_found  = 0;
5150     LARGE_INTEGER RangeEnd;
5151
5152     osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset 0x%x length 0x%x",
5153              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
5154     osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
5155              key.process_id, key.session_id, key.file_id, flags);
5156
5157     if (!exact_match)
5158         RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
5159
5160   try_again:
5161     lock_ObtainRead(&cm_scacheLock);
5162
5163     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5164         fileLock = (cm_file_lock_t *)
5165             ((char *) q - offsetof(cm_file_lock_t, fileq));
5166
5167 #ifdef DEBUG
5168         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5169             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5170             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5171                      fileLock->fid.cell,
5172                      fileLock->fid.volume,
5173                      fileLock->fid.vnode,
5174                      fileLock->fid.unique);
5175             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5176                      fileLock->scp->fid.cell,
5177                      fileLock->scp->fid.volume,
5178                      fileLock->scp->fid.vnode,
5179                      fileLock->scp->fid.unique);
5180             osi_assertx(FALSE, "invalid fid value");
5181         }
5182 #endif
5183         if (exact_match) {
5184             if (!IS_LOCK_DELETED(fileLock) &&
5185                  cm_KeyEquals(&fileLock->key, &key, 0) &&
5186                  fileLock->range.offset == LOffset.QuadPart &&
5187                  fileLock->range.length == LLength.QuadPart) {
5188                 lock_found = 1;
5189                 break;
5190             }
5191         } else {
5192
5193             if (!IS_LOCK_DELETED(fileLock) &&
5194                  cm_KeyEquals(&fileLock->key, &key, 0) &&
5195                  fileLock->range.offset >= LOffset.QuadPart &&
5196                  fileLock->range.offset < RangeEnd.QuadPart &&
5197                  (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5198                 lock_found = 1;
5199                 break;
5200             }
5201         }
5202     }
5203
5204     if (!q) {
5205         lock_ReleaseRead(&cm_scacheLock);
5206
5207         if (lock_found && !exact_match) {
5208             code = 0;
5209             goto done;
5210         } else {
5211             osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5212
5213             /* The lock didn't exist anyway. *shrug* */
5214             return CM_ERROR_RANGE_NOT_LOCKED;
5215         }
5216     }
5217
5218     /* discard lock record */
5219     lock_ConvertRToW(&cm_scacheLock);
5220     osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5221
5222     /*
5223      * Don't delete it here; let the daemon delete it, to simplify
5224      * the daemon's traversal of the list.
5225      */
5226
5227     if (IS_LOCK_CLIENTONLY(fileLock)) {
5228         scp->clientLocks--;
5229     } else if (IS_LOCK_ACCEPTED(fileLock)) {
5230         if (fileLock->lockType == LockRead)
5231             scp->sharedLocks--;
5232         else
5233             scp->exclusiveLocks--;
5234     }
5235
5236     fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5237     lock_ReleaseWrite(&cm_scacheLock);
5238
5239     if (release_userp) {
5240         cm_ReleaseUser(userp);
5241         release_userp = FALSE;
5242     }
5243
5244     if (!exact_match) {
5245         osi_Log1(afsd_logp, "cm_Unlock not exact match, searching for next lock, code 0x%x", code);
5246         goto try_again;         /* might be more than one lock in the range */
5247     }
5248
5249  done:
5250
5251     osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5252     osi_Log4(afsd_logp, "  leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5253              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5254              (int)(signed char) scp->serverLock);
5255
5256     return code;
5257 }
5258
5259 /* called with scp->rw held */
5260 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5261 {
5262     cm_file_lock_t *fileLock;
5263     osi_queue_t *q;
5264
5265     osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5266
5267     /* cm_scacheLock needed because we are modifying fileLock->flags */
5268     lock_ObtainWrite(&cm_scacheLock);
5269
5270     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5271         fileLock =
5272             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5273
5274         if (IS_LOCK_ACTIVE(fileLock) &&
5275             !IS_LOCK_CLIENTONLY(fileLock)) {
5276             if (fileLock->lockType == LockRead)
5277                 scp->sharedLocks--;
5278             else
5279                 scp->exclusiveLocks--;
5280
5281             fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5282         }
5283     }
5284
5285     scp->serverLock = -1;
5286     scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5287     lock_ReleaseWrite(&cm_scacheLock);
5288 }
5289
5290 /* Called with no relevant locks held */
5291 void cm_CheckLocks()
5292 {
5293     osi_queue_t *q, *nq;
5294     cm_file_lock_t *fileLock;
5295     cm_req_t req;
5296     AFSFid tfid;
5297     AFSVolSync volSync;
5298     cm_conn_t *connp;
5299     long code;
5300     struct rx_connection * rxconnp;
5301     cm_scache_t * scp;
5302
5303     memset(&volSync, 0, sizeof(volSync));
5304
5305     cm_InitReq(&req);
5306
5307     lock_ObtainWrite(&cm_scacheLock);
5308
5309     cm_lockRefreshCycle++;
5310
5311     osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5312
5313     for (q = cm_allFileLocks; q; q = nq) {
5314         fileLock = (cm_file_lock_t *) q;
5315         nq = osi_QNext(q);
5316         code = -1;
5317
5318         if (IS_LOCK_DELETED(fileLock)) {
5319             cm_user_t *userp = fileLock->userp;
5320             cm_scache_t *scp = fileLock->scp;
5321             fileLock->userp = NULL;
5322             fileLock->scp = NULL;
5323
5324             lock_ReleaseWrite(&cm_scacheLock);
5325             lock_ObtainWrite(&scp->rw);
5326             code = cm_IntUnlock(scp, userp, &req);
5327             lock_ReleaseWrite(&scp->rw);
5328
5329             cm_ReleaseUser(fileLock->userp);
5330             lock_ObtainWrite(&cm_scacheLock);
5331             cm_ReleaseSCacheNoLock(scp);
5332
5333             osi_QRemove(&cm_allFileLocks, q);
5334             cm_PutFileLock(fileLock);
5335
5336         } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5337
5338             /* Server locks must have been enabled for us to have
5339                received an active non-client-only lock. */
5340             osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5341
5342             scp = fileLock->scp;
5343             osi_assertx(scp != NULL, "null cm_scache_t");
5344
5345             cm_HoldSCacheNoLock(scp);
5346
5347 #ifdef DEBUG
5348             if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5349                 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5350                 osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5351                          fileLock->fid.cell,
5352                          fileLock->fid.volume,
5353                          fileLock->fid.vnode,
5354                          fileLock->fid.unique);
5355                 osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5356                          fileLock->scp->fid.cell,
5357                          fileLock->scp->fid.volume,
5358                          fileLock->scp->fid.vnode,
5359                          fileLock->scp->fid.unique);
5360                 osi_assertx(FALSE, "invalid fid");
5361             }
5362 #endif
5363             /* Server locks are extended once per scp per refresh
5364                cycle. */
5365             if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5366
5367                 int scp_done = FALSE;
5368
5369                 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5370
5371                 lock_ReleaseWrite(&cm_scacheLock);
5372                 lock_ObtainWrite(&scp->rw);
5373
5374                 /* did the lock change while we weren't holding the lock? */
5375                 if (!IS_LOCK_ACTIVE(fileLock))
5376                     goto post_syncopdone;
5377
5378                 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5379                                  CM_SCACHESYNC_NEEDCALLBACK
5380                                  | CM_SCACHESYNC_GETSTATUS
5381                                  | CM_SCACHESYNC_LOCK);
5382
5383                 if (code) {
5384                     osi_Log1(afsd_logp,
5385                              "cm_CheckLocks SyncOp failure code 0x%x", code);
5386                     goto post_syncopdone;
5387                 }
5388
5389                 /* cm_SyncOp releases scp->rw during which the lock
5390                    may get released. */
5391                 if (!IS_LOCK_ACTIVE(fileLock))
5392                     goto pre_syncopdone;
5393
5394                 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5395                     cm_fid_t cfid;
5396                     cm_user_t * userp;
5397
5398                     tfid.Volume = scp->fid.volume;
5399                     tfid.Vnode = scp->fid.vnode;
5400                     tfid.Unique = scp->fid.unique;
5401                     cfid = scp->fid;
5402                     userp = fileLock->userp;
5403
5404                     osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5405                              fileLock,
5406                              scp,
5407                              (int) scp->serverLock);
5408
5409                     lock_ReleaseWrite(&scp->rw);
5410
5411                     do {
5412                         code = cm_ConnFromFID(&cfid, userp,
5413                                        &req, &connp);
5414                         if (code)
5415                             break;
5416
5417                         rxconnp = cm_GetRxConn(connp);
5418                         code = RXAFS_ExtendLock(rxconnp, &tfid,
5419                                                 &volSync);
5420                         rx_PutConnection(rxconnp);
5421
5422                         osi_Log1(afsd_logp, "   ExtendLock returns %d", code);
5423
5424                     } while (cm_Analyze(connp, userp, &req,
5425                                         &cfid, &volSync, NULL, NULL,
5426                                         code));
5427
5428                     code = cm_MapRPCError(code, &req);
5429
5430                     lock_ObtainWrite(&scp->rw);
5431
5432                     if (code) {
5433                         osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5434                         scp->fsLockCount = 0;
5435                     } else {
5436                         osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5437                         scp->lockDataVersion = scp->dataVersion;
5438                     }
5439
5440                     if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5441                         scp->lockDataVersion == scp->dataVersion) {
5442                         int lockType;
5443
5444                         lockType =
5445                             (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5446
5447                         /* we might still have a chance to obtain a
5448                            new lock */
5449
5450                         code = cm_IntSetLock(scp, userp, lockType, &req);
5451
5452                         if (code) {
5453                             code = CM_ERROR_INVAL;
5454                         } else if (scp->lockDataVersion != scp->dataVersion) {
5455
5456                             /* now check if we still have the file at
5457                                the right data version. */
5458                             osi_Log1(afsd_logp,
5459                                      "Data version mismatch on scp 0x%p",
5460                                      scp);
5461                             osi_Log2(afsd_logp,
5462                                      "   Data versions: before=%I64d, after=%I64d",
5463                                      scp->lockDataVersion,
5464                                      scp->dataVersion);
5465
5466                             code = cm_IntReleaseLock(scp, userp, &req);
5467
5468                             code = CM_ERROR_INVAL;
5469                         }
5470                     }
5471
5472                     if (code == EINVAL || code == CM_ERROR_INVAL ||
5473                         code == CM_ERROR_BADFD) {
5474                         cm_LockMarkSCacheLost(scp);
5475                     }
5476
5477                 } else {
5478                     /* interestingly, we have found an active lock
5479                        belonging to an scache that has no
5480                        serverLock */
5481                     cm_LockMarkSCacheLost(scp);
5482                 }
5483
5484                 scp_done = TRUE;
5485
5486             pre_syncopdone:
5487
5488                 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5489
5490             post_syncopdone:
5491                 lock_ReleaseWrite(&scp->rw);
5492
5493                 lock_ObtainWrite(&cm_scacheLock);
5494
5495                 if (code == 0) {
5496                     fileLock->lastUpdate = time(NULL);
5497                 }
5498
5499                 if (scp_done)
5500                     scp->lastRefreshCycle = cm_lockRefreshCycle;
5501
5502             } else {
5503                 /* we have already refreshed the locks on this scp */
5504                 fileLock->lastUpdate = time(NULL);
5505             }
5506
5507             cm_ReleaseSCacheNoLock(scp);
5508
5509         } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5510             /* TODO: Check callbacks */
5511         }
5512     }
5513
5514     lock_ReleaseWrite(&cm_scacheLock);
5515     osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5516 }
5517
5518 /* NOT called with scp->rw held. */
5519 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5520 {
5521     long code = 0;
5522     cm_scache_t *scp = NULL;
5523     cm_file_lock_t *fileLock;
5524     osi_queue_t *q;
5525     cm_req_t req;
5526     int newLock = -1;
5527     int force_client_lock = FALSE;
5528     int has_insert = FALSE;
5529     int check_data_version = FALSE;
5530
5531     cm_InitReq(&req);
5532
5533     if (client_is_dead) {
5534         code = CM_ERROR_TIMEDOUT;
5535         goto updateLock;
5536     }
5537
5538     lock_ObtainRead(&cm_scacheLock);
5539
5540     osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5541     osi_Log4(afsd_logp, "    offset(%x:%x) length(%x:%x)",
5542              (unsigned)(oldFileLock->range.offset >> 32),
5543              (unsigned)(oldFileLock->range.offset & 0xffffffff),
5544              (unsigned)(oldFileLock->range.length >> 32),
5545              (unsigned)(oldFileLock->range.length & 0xffffffff));
5546     osi_Log4(afsd_logp, "    key<0x%x,0x%x,0x%x> flags=%x",
5547              oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5548              (unsigned)(oldFileLock->flags));
5549
5550     /* if the lock has already been granted, then we have nothing to do */
5551     if (IS_LOCK_ACTIVE(oldFileLock)) {
5552         lock_ReleaseRead(&cm_scacheLock);
5553         osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5554         return 0;
5555     }
5556
5557     /* we can't do anything with lost or deleted locks at the moment. */
5558     if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5559         code = CM_ERROR_BADFD;
5560         osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5561         lock_ReleaseRead(&cm_scacheLock);
5562         goto updateLock;
5563     }
5564
5565     scp = oldFileLock->scp;
5566
5567     osi_assertx(scp != NULL, "null cm_scache_t");
5568
5569     lock_ReleaseRead(&cm_scacheLock);
5570     lock_ObtainWrite(&scp->rw);
5571
5572     code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5573                              oldFileLock->userp,
5574                              &req, &has_insert);
5575
5576     if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5577         if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5578         force_client_lock = TRUE;
5579         }
5580         code = 0;
5581     } else if (code) {
5582         lock_ReleaseWrite(&scp->rw);
5583         return code;
5584     }
5585
5586     lock_ObtainWrite(&cm_scacheLock);
5587
5588     /* Check if we already have a sufficient server lock to allow this
5589        lock to go through. */
5590     if (IS_LOCK_WAITLOCK(oldFileLock) &&
5591         (!SERVERLOCKS_ENABLED(scp) ||
5592          scp->serverLock == oldFileLock->lockType ||
5593          scp->serverLock == LockWrite)) {
5594
5595         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5596
5597         if (SERVERLOCKS_ENABLED(scp)) {
5598             osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock.  Granting",
5599                      (int) scp->serverLock);
5600         } else {
5601             osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5602         }
5603
5604         lock_ReleaseWrite(&cm_scacheLock);
5605         lock_ReleaseWrite(&scp->rw);
5606
5607         return 0;
5608     }
5609
5610     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5611
5612         /* check if the conflicting locks have dissappeared already */
5613         for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5614
5615             fileLock = (cm_file_lock_t *)
5616                 ((char *) q - offsetof(cm_file_lock_t, fileq));
5617
5618             if (IS_LOCK_LOST(fileLock)) {
5619                 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5620                     code = CM_ERROR_BADFD;
5621                     oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5622                     osi_Log1(afsd_logp, "    found lost lock %p for same key.  Marking lock as lost",
5623                              fileLock);
5624                     break;
5625                 } else if (fileLock->lockType == LockWrite &&
5626                            INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5627                     osi_Log1(afsd_logp, "    found conflicting LOST lock %p", fileLock);
5628                     code = CM_ERROR_WOULDBLOCK;
5629                     break;
5630                 }
5631             }
5632
5633             if (IS_LOCK_ACCEPTED(fileLock) &&
5634                 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5635
5636                 if (oldFileLock->lockType != LockRead ||
5637                    fileLock->lockType != LockRead) {
5638
5639                     osi_Log1(afsd_logp, "    found conflicting lock %p", fileLock);
5640                     code = CM_ERROR_WOULDBLOCK;
5641                     break;
5642                 }
5643             }
5644         }
5645     }
5646
5647     if (code != 0) {
5648         lock_ReleaseWrite(&cm_scacheLock);
5649         lock_ReleaseWrite(&scp->rw);
5650
5651         goto handleCode;
5652     }
5653
5654     /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5655        If it is WAITUNLOCK, then we didn't find any conflicting lock
5656        but we haven't verfied whether the serverLock is sufficient to
5657        assert it.  If it is WAITLOCK, then the serverLock is
5658        insufficient to assert it. Eitherway, we are ready to accept
5659        the lock as either ACTIVE or WAITLOCK depending on the
5660        serverLock. */
5661
5662     /* First, promote the WAITUNLOCK to a WAITLOCK */
5663     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5664         if (oldFileLock->lockType == LockRead)
5665             scp->sharedLocks++;
5666         else
5667             scp->exclusiveLocks++;
5668
5669         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5670         oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5671     }
5672
5673     osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5674
5675     if (force_client_lock ||
5676         !SERVERLOCKS_ENABLED(scp) ||
5677         scp->serverLock == oldFileLock->lockType ||
5678         (oldFileLock->lockType == LockRead &&
5679          scp->serverLock == LockWrite)) {
5680
5681         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5682
5683         if ((force_client_lock ||
5684              !SERVERLOCKS_ENABLED(scp)) &&
5685             !IS_LOCK_CLIENTONLY(oldFileLock)) {
5686
5687             oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5688
5689             if (oldFileLock->lockType == LockRead)
5690                 scp->sharedLocks--;
5691             else
5692                 scp->exclusiveLocks--;
5693
5694             scp->clientLocks++;
5695         }
5696
5697         lock_ReleaseWrite(&cm_scacheLock);
5698         lock_ReleaseWrite(&scp->rw);
5699
5700         return 0;
5701
5702     } else {
5703         cm_user_t * userp;
5704
5705         code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5706                          CM_SCACHESYNC_NEEDCALLBACK
5707                          | CM_SCACHESYNC_GETSTATUS
5708                          | CM_SCACHESYNC_LOCK);
5709         if (code) {
5710             osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5711             lock_ReleaseWrite(&cm_scacheLock);
5712             goto post_syncopdone;
5713         }
5714
5715         if (!IS_LOCK_WAITLOCK(oldFileLock))
5716             goto pre_syncopdone;
5717
5718         userp = oldFileLock->userp;
5719
5720 #ifndef AGGRESSIVE_LOCKS
5721         newLock = oldFileLock->lockType;
5722 #else
5723         newLock = LockWrite;
5724 #endif
5725
5726         if (has_insert) {
5727             /* if has_insert is non-zero, then:
5728                - the lock a LockRead
5729                - we don't have permission to get a LockRead
5730                - we do have permission to get a LockWrite
5731                - the server supports VICED_CAPABILITY_WRITELOCKACL
5732             */
5733
5734             newLock = LockWrite;
5735         }
5736
5737         lock_ReleaseWrite(&cm_scacheLock);
5738
5739         /* when we get here, either we have a read-lock and want a
5740            write-lock or we don't have any locks and we want some
5741            lock. */
5742
5743         if (scp->serverLock == LockRead) {
5744
5745             osi_assertx(newLock == LockWrite, "!LockWrite");
5746
5747             osi_Log0(afsd_logp, "  Attempting to UPGRADE from LockRead to LockWrite");
5748
5749             scp->lockDataVersion = scp->dataVersion;
5750             check_data_version = TRUE;
5751
5752             code = cm_IntReleaseLock(scp, userp, &req);
5753
5754             if (code)
5755                 goto pre_syncopdone;
5756             else
5757                 scp->serverLock = -1;
5758         }
5759
5760         code = cm_IntSetLock(scp, userp, newLock, &req);
5761
5762         if (code == 0) {
5763             if (scp->dataVersion != scp->lockDataVersion) {
5764                 /* we lost a race.  too bad */
5765
5766                 osi_Log0(afsd_logp,
5767                          "  Data version mismatch while upgrading lock.");
5768                 osi_Log2(afsd_logp,
5769                          "  Data versions before=%I64d, after=%I64d",
5770                          scp->lockDataVersion,
5771                          scp->dataVersion);
5772                 osi_Log1(afsd_logp,
5773                          "  Releasing stale lock for scp 0x%x", scp);
5774
5775                 code = cm_IntReleaseLock(scp, userp, &req);
5776
5777                 scp->serverLock = -1;
5778
5779                 code = CM_ERROR_INVAL;
5780
5781                 cm_LockMarkSCacheLost(scp);
5782             } else {
5783                 scp->serverLock = newLock;
5784             }
5785         }
5786
5787     pre_syncopdone:
5788         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5789     post_syncopdone:
5790         ;
5791     }
5792
5793   handleCode:
5794     if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5795         lock_ObtainWrite(&cm_scacheLock);
5796         osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5797         lock_ReleaseWrite(&cm_scacheLock);
5798     }
5799     lock_ReleaseWrite(&scp->rw);
5800
5801   updateLock:
5802     lock_ObtainWrite(&cm_scacheLock);
5803     if (code == 0) {
5804         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5805     } else if (code != CM_ERROR_WOULDBLOCK) {
5806         oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5807         cm_ReleaseUser(oldFileLock->userp);
5808         oldFileLock->userp = NULL;
5809         if (oldFileLock->scp) {
5810             cm_ReleaseSCacheNoLock(oldFileLock->scp);
5811             oldFileLock->scp = NULL;
5812         }
5813     }
5814     lock_ReleaseWrite(&cm_scacheLock);
5815
5816     return code;
5817 }
5818
5819 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
5820 {
5821     cm_key_t key;
5822
5823     key.process_id = process_id;
5824     key.session_id = session_id;
5825     key.file_id = file_id;
5826
5827     return key;
5828 }
5829
5830 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5831 {
5832     return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5833         ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
5834 }
5835
5836 void cm_ReleaseAllLocks(void)
5837 {
5838     cm_scache_t *scp;
5839     cm_req_t req;
5840     cm_user_t *userp;
5841     cm_key_t   key;
5842     cm_file_lock_t *fileLock;
5843     unsigned int i;
5844
5845     for (i = 0; i < cm_data.scacheHashTableSize; i++)
5846     {
5847         for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5848             while (scp->fileLocksH != NULL) {
5849                 lock_ObtainWrite(&scp->rw);
5850                 lock_ObtainWrite(&cm_scacheLock);
5851                 if (!scp->fileLocksH) {
5852                     lock_ReleaseWrite(&cm_scacheLock);
5853                     lock_ReleaseWrite(&scp->rw);
5854                     break;
5855                 }
5856                 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5857                 userp = fileLock->userp;
5858                 cm_HoldUser(userp);
5859                 key = fileLock->key;
5860                 cm_HoldSCacheNoLock(scp);
5861                 lock_ReleaseWrite(&cm_scacheLock);
5862                 cm_UnlockByKey(scp, key, 0, userp, &req);
5863                 cm_ReleaseSCache(scp);
5864                 cm_ReleaseUser(userp);
5865                 lock_ReleaseWrite(&scp->rw);
5866             }
5867         }
5868     }
5869 }