windows-misc-20080806
[openafs.git] / src / WINNT / afsd / cm_vnodeops.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afs/param.h>
11 #include <afs/stds.h>
12
13 #include <windows.h>
14 #include <winsock2.h>
15 #include <stddef.h>
16 #include <malloc.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <errno.h>
20
21 #include <osi.h>
22
23 #include "afsd.h"
24 #include "smb.h"
25 #include "cm_btree.h"
26
27 #include <strsafe.h>
28
29 #ifdef DEBUG
30 extern void afsi_log(char *pattern, ...);
31 #endif
32
33 int cm_enableServerLocks = 1;
34
35 int cm_followBackupPath = 0;
36
37 /*
38  * Case-folding array.  This was constructed by inspecting of SMBtrace output.
39  * I do not know anything more about it.
40  */
41 unsigned char cm_foldUpper[256] = {
42      0x0,  0x1,  0x2,  0x3,  0x4,  0x5,  0x6,  0x7,
43      0x8,  0x9,  0xa,  0xb,  0xc,  0xd,  0xe,  0xf,
44     0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
45     0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
46     0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
47     0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
48     0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
49     0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
50     0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
51     0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
52     0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
53     0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
54     0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
55     0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
56     0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
57     0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
58     0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
59     0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
60     0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
61     0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
62     0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
63     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
64     0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
65     0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
66     0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
67     0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
68     0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
69     0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
70     0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
71     0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
72     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
73     0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
74 };
75
76 /*
77  * Case-insensitive string comparison.  We used to use stricmp, but it doesn't
78  * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
79  * upper-case u-umlaut).
80  */
81 int cm_stricmp(const char *str1, const char *str2)
82 {
83     char c1, c2;
84
85     while (1) {
86         if (*str1 == 0)
87             if (*str2 == 0)
88                 return 0;
89             else
90                 return -1;
91         if (*str2 == 0)
92             return 1;
93         c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
94         c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
95         if (c1 < c2)
96             return -1;
97         if (c1 > c2)
98             return 1;
99     }
100 }
101
102
103
104 /* return success if we can open this file in this mode */
105 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
106                   cm_req_t *reqp)
107 {
108     long rights;
109     long code;
110
111     rights = 0;
112     if (openMode != 1) 
113         rights |= PRSFS_READ;
114     if (openMode == 1 || openMode == 2 || trunc) 
115         rights |= PRSFS_WRITE;
116         
117     lock_ObtainWrite(&scp->rw);
118
119     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
120                       CM_SCACHESYNC_GETSTATUS
121                      | CM_SCACHESYNC_NEEDCALLBACK
122                      | CM_SCACHESYNC_LOCK);
123
124     if (code == 0 && 
125         ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
126         scp->fileType == CM_SCACHETYPE_FILE) {
127
128         cm_key_t key;
129         unsigned int sLockType;
130         LARGE_INTEGER LOffset, LLength;
131
132         /* Check if there's some sort of lock on the file at the
133            moment. */
134
135         key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
136
137         if (rights & PRSFS_WRITE)
138             sLockType = 0;
139         else
140             sLockType = LOCKING_ANDX_SHARED_LOCK;
141
142         LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
143         LOffset.LowPart  = CM_FLSHARE_OFFSET_LOW;
144         LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
145         LLength.LowPart  = CM_FLSHARE_LENGTH_LOW;
146
147         code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
148
149         if (code == 0) {
150             cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
151         } else {
152             /* In this case, we allow the file open to go through even
153                though we can't enforce mandatory locking on the
154                file. */
155             if (code == CM_ERROR_NOACCESS &&
156                 !(rights & PRSFS_WRITE))
157                 code = 0;
158             else {
159                 switch (code) {
160                 case CM_ERROR_ALLOFFLINE:
161                 case CM_ERROR_ALLDOWN:
162                 case CM_ERROR_ALLBUSY:
163                 case CM_ERROR_TIMEDOUT:
164                 case CM_ERROR_RETRY:
165                 case CM_ERROR_WOULDBLOCK:
166                     break;
167                 default:
168                     code = CM_ERROR_SHARING_VIOLATION;
169                 }
170             }
171         }
172
173     } else if (code != 0) {
174         goto _done;
175     }
176
177     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
178
179  _done:
180
181     lock_ReleaseWrite(&scp->rw);
182
183     return code;
184 }
185
186 /* return success if we can open this file in this mode */
187 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
188                     unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp, 
189                     cm_lock_data_t **ldpp)
190 {
191     long rights;
192     long code;
193
194     osi_assertx(ldpp != NULL, "null cm_lock_data_t");
195     *ldpp = NULL;
196
197     /* Always allow delete; the RPC will tell us if it's OK */
198     if (desiredAccess == DELETE)
199         return 0;
200
201     rights = 0;
202
203     if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
204         rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
205
206     /* We used to require PRSFS_WRITE if createDisp was 4
207        (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
208        However, we don't need to do that since the existence of the
209        scp implies that we don't need to create it. */
210     if (desiredAccess & AFS_ACCESS_WRITE)
211         rights |= PRSFS_WRITE;
212
213     lock_ObtainWrite(&scp->rw);
214
215     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
216                       CM_SCACHESYNC_GETSTATUS
217                      | CM_SCACHESYNC_NEEDCALLBACK
218                      | CM_SCACHESYNC_LOCK);
219
220     /*
221      * If the open will fail because the volume is readonly, then we will
222      * return an access denied error instead.  This is to help brain-dead
223      * apps run correctly on replicated volumes.
224      * See defect 10007 for more information.
225      */
226     if (code == CM_ERROR_READONLY)
227         code = CM_ERROR_NOACCESS;
228
229     if (code == 0 &&
230              ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
231              scp->fileType == CM_SCACHETYPE_FILE) {
232         cm_key_t key;
233         unsigned int sLockType;
234         LARGE_INTEGER LOffset, LLength;
235
236         /* Check if there's some sort of lock on the file at the
237            moment. */
238
239         key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
240         if (rights & PRSFS_WRITE)
241             sLockType = 0;
242         else
243             sLockType = LOCKING_ANDX_SHARED_LOCK;
244
245         /* single byte lock at offset 0x0100 0000 0000 0000 */
246         LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
247         LOffset.LowPart  = CM_FLSHARE_OFFSET_LOW;
248         LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
249         LLength.LowPart  = CM_FLSHARE_LENGTH_LOW;
250
251         code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
252
253         if (code == 0) {
254             (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
255             if (!*ldpp) {
256                 code = ENOMEM;
257                 goto _syncopdone;
258             }
259
260             (*ldpp)->key = key;
261             (*ldpp)->sLockType = sLockType;
262             (*ldpp)->LOffset.HighPart = LOffset.HighPart;
263             (*ldpp)->LOffset.LowPart = LOffset.LowPart;
264             (*ldpp)->LLength.HighPart = LLength.HighPart;
265             (*ldpp)->LLength.LowPart = LLength.LowPart;
266         } else {
267             /* In this case, we allow the file open to go through even
268                though we can't enforce mandatory locking on the
269                file. */
270             if (code == CM_ERROR_NOACCESS &&
271                 !(rights & PRSFS_WRITE))
272                 code = 0;
273             else {
274                 switch (code) {
275                 case CM_ERROR_ALLOFFLINE:
276                 case CM_ERROR_ALLDOWN:
277                 case CM_ERROR_ALLBUSY:
278                 case CM_ERROR_TIMEDOUT:
279                 case CM_ERROR_RETRY:
280                 case CM_ERROR_WOULDBLOCK:
281                     break;
282                 default:
283                     code = CM_ERROR_SHARING_VIOLATION;
284                 }
285             }
286         }
287     } else if (code != 0) {
288         goto _done;
289     }
290
291   _syncopdone:
292     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
293
294  _done:
295     lock_ReleaseWrite(&scp->rw);
296
297     osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
298     return code;
299 }
300
301 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, 
302                                cm_lock_data_t ** ldpp)
303 {
304     osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
305     if (*ldpp) {
306         lock_ObtainWrite(&scp->rw);
307         cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength, 
308                   (*ldpp)->key, userp, reqp);
309         lock_ReleaseWrite(&scp->rw);
310         free(*ldpp);
311         *ldpp = NULL;
312     }
313     return 0;
314 }
315 /*
316  * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
317  * done in three steps:
318  * (1) open for deletion (NT_CREATE_AND_X)
319  * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
320  * (3) close (CLOSE)
321  * We must not do the RPC until step 3.  But if we are going to return an error
322  * code (e.g. directory not empty), we must return it by step 2, otherwise most
323  * clients will not notice it.  So we do a preliminary check.  For deleting
324  * files, this is almost free, since we have already done the RPC to get the
325  * parent directory's status bits.  But for deleting directories, we must do an
326  * additional RPC to get the directory's data to check if it is empty.  Sigh.
327  */
328 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
329         cm_req_t *reqp)
330 {
331     long code;
332     osi_hyper_t thyper;
333     cm_buf_t *bufferp;
334     cm_dirEntry_t *dep = 0;
335     unsigned short *hashTable;
336     unsigned int i, idx;
337     int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
338     int releaseLock = 0;
339
340     /* First check permissions */
341     lock_ObtainWrite(&scp->rw);
342     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
343                       CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
344     if (!code)
345         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
346     lock_ReleaseWrite(&scp->rw);
347     if (code)
348         return code;
349
350     /* If deleting directory, must be empty */
351
352     if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
353         return code;
354
355     thyper.HighPart = 0; thyper.LowPart = 0;
356     code = buf_Get(scp, &thyper, &bufferp);
357     if (code)
358         return code;
359
360     lock_ObtainMutex(&bufferp->mx);
361     lock_ObtainWrite(&scp->rw);
362     releaseLock = 1;
363     while (1) {
364         code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
365                           CM_SCACHESYNC_NEEDCALLBACK
366                           | CM_SCACHESYNC_READ
367                           | CM_SCACHESYNC_BUFLOCKED);
368         if (code)
369             goto done;
370
371         if (cm_HaveBuffer(scp, bufferp, 1))
372             break;
373
374         /* otherwise, load the buffer and try again */
375         lock_ReleaseMutex(&bufferp->mx);
376         code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
377         lock_ReleaseWrite(&scp->rw);
378         lock_ObtainMutex(&bufferp->mx);
379         lock_ObtainWrite(&scp->rw);
380         cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
381         if (code)
382             goto done;
383     }
384
385     lock_ReleaseWrite(&scp->rw);
386     releaseLock = 0;
387
388     /* We try to determine emptiness without looking beyond the first page,
389      * and without assuming "." and ".." are present and are on the first
390      * page (though these assumptions might, after all, be reasonable).
391      */
392     hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
393     for (i=0; i<128; i++) {
394         idx = ntohs(hashTable[i]);
395         while (idx) {
396             if (idx >= 64) {
397                 BeyondPage = 1;
398                 break;
399             }
400             dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
401             if (strcmp(dep->name, ".") == 0)
402                 HaveDot = 1;
403             else if (strcmp(dep->name, "..") == 0)
404                 HaveDotDot = 1;
405             else {
406                 code = CM_ERROR_NOTEMPTY;
407                 goto done;
408             }
409             idx = ntohs(dep->next);
410         }
411     }
412     if (BeyondPage && HaveDot && HaveDotDot)
413         code = CM_ERROR_NOTEMPTY;
414     else
415         code = 0;
416   done:   
417     lock_ReleaseMutex(&bufferp->mx);
418     buf_Release(bufferp);
419     if (releaseLock)
420         lock_ReleaseWrite(&scp->rw);
421     return code;
422 }       
423
424 /*
425  * Iterate through all entries in a directory.
426  * When the function funcp is called, the buffer is locked but the
427  * directory vnode is not.
428  *
429  * If the retscp parameter is not NULL, the parmp must be a 
430  * cm_lookupSearch_t object.  
431  */
432 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
433                  osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
434                  cm_scache_t **retscp)
435 {
436     char *tp;
437     long code;
438     cm_dirEntry_t *dep = 0;
439     cm_buf_t *bufferp;
440     long temp;
441     osi_hyper_t dirLength;
442     osi_hyper_t bufferOffset;
443     osi_hyper_t curOffset;
444     osi_hyper_t thyper;
445     long entryInDir;
446     long entryInBuffer;
447     cm_pageHeader_t *pageHeaderp;
448     int slotInPage;
449     long nextEntryCookie;
450     int numDirChunks;   /* # of 32 byte dir chunks in this entry */
451         
452     /* get the directory size */
453     lock_ObtainWrite(&scp->rw);
454     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
455                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
456     lock_ReleaseWrite(&scp->rw);
457     if (code)
458         return code;
459         
460     if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
461         return CM_ERROR_NOTDIR;
462
463     if (retscp)                         /* if this is a lookup call */
464     {
465         cm_lookupSearch_t*      sp = parmp;
466
467         if (
468 #ifdef AFS_FREELANCE_CLIENT
469         /* Freelance entries never end up in the DNLC because they
470          * do not have an associated cm_server_t
471          */
472             !(cm_freelanceEnabled &&
473             sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
474               sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
475 #else /* !AFS_FREELANCE_CLIENT */
476             TRUE
477 #endif
478             ) 
479         {
480             int casefold = sp->caseFold;
481             sp->caseFold = 0; /* we have a strong preference for exact matches */
482             if ( *retscp = cm_dnlcLookup(scp, sp))      /* dnlc hit */
483             {
484                 sp->caseFold = casefold;
485                 return 0;
486             }
487             sp->caseFold = casefold;
488
489             /* see if we can find it using the directory hash tables.
490                we can only do exact matches, since the hash is case
491                sensitive. */
492             {
493                 cm_dirOp_t dirop;
494 #ifdef USE_BPLUS
495                 int usedBplus = 0;
496 #endif
497
498                 code = ENOENT;
499
500                 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
501                 if (code == 0) {
502
503 #ifdef USE_BPLUS
504                     code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
505                     if (code != EINVAL)
506                         usedBplus = 1;
507                     else 
508 #endif
509                         code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
510
511                     cm_EndDirOp(&dirop);
512                 }
513
514                 if (code == 0) {
515                     /* found it */
516                     sp->found = TRUE;
517                     sp->ExactFound = TRUE;
518                     *retscp = NULL; /* force caller to call cm_GetSCache() */
519                     return 0;
520                 }
521 #ifdef USE_BPLUS
522                 if (usedBplus) {
523                     if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
524                         /* found it */
525                         sp->found = TRUE;
526                         sp->ExactFound = FALSE;
527                         *retscp = NULL; /* force caller to call cm_GetSCache() */
528                         return 0;
529                     }
530                     
531                     return CM_ERROR_BPLUS_NOMATCH;
532                 }
533 #endif 
534             }
535         }
536     }   
537
538     /*
539      * XXX We only get the length once.  It might change when we drop the
540      * lock.
541      */
542     dirLength = scp->length;
543
544     bufferp = NULL;
545     bufferOffset.LowPart = bufferOffset.HighPart = 0;
546     if (startOffsetp)
547         curOffset = *startOffsetp;
548     else {
549         curOffset.HighPart = 0;
550         curOffset.LowPart = 0;
551     }   
552
553     while (1) {
554         /* make sure that curOffset.LowPart doesn't point to the first
555          * 32 bytes in the 2nd through last dir page, and that it
556          * doesn't point at the first 13 32-byte chunks in the first
557          * dir page, since those are dir and page headers, and don't
558          * contain useful information.
559          */
560         temp = curOffset.LowPart & (2048-1);
561         if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
562             /* we're in the first page */
563             if (temp < 13*32) temp = 13*32;
564         }
565         else {
566             /* we're in a later dir page */
567             if (temp < 32) temp = 32;
568         }       
569                 
570         /* make sure the low order 5 bits are zero */
571         temp &= ~(32-1);
572                 
573         /* now put temp bits back ito curOffset.LowPart */
574         curOffset.LowPart &= ~(2048-1);
575         curOffset.LowPart |= temp;
576
577         /* check if we've passed the dir's EOF */
578         if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
579             break;
580                 
581         /* see if we can use the bufferp we have now; compute in which
582          * page the current offset would be, and check whether that's
583          * the offset of the buffer we have.  If not, get the buffer.
584          */
585         thyper.HighPart = curOffset.HighPart;
586         thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
587         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
588             /* wrong buffer */
589             if (bufferp) {
590                 lock_ReleaseMutex(&bufferp->mx);
591                 buf_Release(bufferp);
592                 bufferp = NULL;
593             }
594
595             code = buf_Get(scp, &thyper, &bufferp);
596             if (code) {
597                 /* if buf_Get() fails we do not have a buffer object to lock */
598                 bufferp = NULL;
599                 break;
600             }
601
602             lock_ObtainMutex(&bufferp->mx);
603             bufferOffset = thyper;
604
605             /* now get the data in the cache */
606             while (1) {
607                 lock_ObtainWrite(&scp->rw);
608                 code = cm_SyncOp(scp, bufferp, userp, reqp,
609                                   PRSFS_LOOKUP,
610                                   CM_SCACHESYNC_NEEDCALLBACK
611                                   | CM_SCACHESYNC_READ
612                                   | CM_SCACHESYNC_BUFLOCKED);
613                 if (code) {
614                     lock_ReleaseWrite(&scp->rw);
615                     break;
616                 }
617                 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
618                                 
619                 if (cm_HaveBuffer(scp, bufferp, 1)) {
620                     lock_ReleaseWrite(&scp->rw);
621                     break;
622                 }
623
624                 /* otherwise, load the buffer and try again */
625                 lock_ReleaseMutex(&bufferp->mx);
626                 code = cm_GetBuffer(scp, bufferp, NULL, userp,
627                                     reqp);
628                 lock_ReleaseWrite(&scp->rw);
629                 lock_ObtainMutex(&bufferp->mx);
630                 if (code) 
631                     break;
632             }
633             if (code) {
634                 lock_ReleaseMutex(&bufferp->mx);
635                 buf_Release(bufferp);
636                 bufferp = NULL;
637                 break;
638             }
639         }       /* if (wrong buffer) ... */
640            
641         /* now we have the buffer containing the entry we're interested
642          * in; copy it out if it represents a non-deleted entry.
643          */
644         entryInDir = curOffset.LowPart & (2048-1);
645         entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
646
647         /* page header will help tell us which entries are free.  Page
648          * header can change more often than once per buffer, since
649          * AFS 3 dir page size may be less than (but not more than) a
650          * buffer package buffer.
651          */
652         /* only look intra-buffer */
653         temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
654         temp &= ~(2048 - 1);    /* turn off intra-page bits */
655         pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
656
657         /* now determine which entry we're looking at in the page.  If
658          * it is free (there's a free bitmap at the start of the dir),
659          * we should skip these 32 bytes.
660          */
661         slotInPage = (entryInDir & 0x7e0) >> 5;
662         if (!(pageHeaderp->freeBitmap[slotInPage>>3]
663                & (1 << (slotInPage & 0x7)))) {
664             /* this entry is free */
665             numDirChunks = 1;   /* only skip this guy */
666             goto nextEntry;
667         }
668
669         tp = bufferp->datap + entryInBuffer;
670         dep = (cm_dirEntry_t *) tp;     /* now points to AFS3 dir entry */
671
672         /* while we're here, compute the next entry's location, too,
673          * since we'll need it when writing out the cookie into the
674          * dir listing stream.
675          */
676         numDirChunks = cm_NameEntries(dep->name, NULL);
677                 
678         /* compute the offset of the cookie representing the next entry */
679         nextEntryCookie = curOffset.LowPart
680             + (CM_DIR_CHUNKSIZE * numDirChunks);
681
682         if (dep->fid.vnode != 0) {
683             /* this is one of the entries to use: it is not deleted */
684             code = (*funcp)(scp, dep, parmp, &curOffset);
685             if (code) 
686                 break;
687         }       /* if we're including this name */
688                 
689       nextEntry:
690         /* and adjust curOffset to be where the new cookie is */
691         thyper.HighPart = 0;
692         thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
693         curOffset = LargeIntegerAdd(thyper, curOffset);
694     }           /* while copying data for dir listing */
695
696     /* release the mutex */
697     if (bufferp) {
698         lock_ReleaseMutex(&bufferp->mx);
699         buf_Release(bufferp);
700     }
701     return code;
702 }
703
704 int cm_NoneUpper(normchar_t *s)
705 {
706     normchar_t c;
707     while (c = *s++)
708         if (c >= 'A' && c <= 'Z')
709             return 0;
710     return 1;
711 }
712
713 int cm_NoneLower(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 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
723                          osi_hyper_t *offp)
724 {
725     cm_lookupSearch_t *sp;
726     int match;
727     normchar_t matchName[MAX_PATH];
728     int looking_for_short_name = FALSE;
729
730     sp = (cm_lookupSearch_t *) rockp;
731
732     cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName));
733     if (sp->caseFold)
734         match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
735     else
736         match = cm_NormStrCmp(matchName, sp->nsearchNamep);
737
738     if (match != 0
739         && sp->hasTilde
740         && !cm_Is8Dot3(matchName)) {
741
742         cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
743         if (sp->caseFold)
744             match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
745         else
746             match = cm_NormStrCmp(matchName, sp->nsearchNamep);
747         looking_for_short_name = TRUE;
748     }
749
750     if (match != 0)
751         return 0;
752
753     sp->found = 1;
754     if (!sp->caseFold) 
755         sp->ExactFound = 1;
756
757     if (!sp->caseFold || looking_for_short_name) {
758         cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
759         return CM_ERROR_STOPNOW;
760     }
761
762     /*
763      * If we get here, we are doing a case-insensitive search, and we
764      * have found a match.  Now we determine what kind of match it is:
765      * exact, lower-case, upper-case, or none of the above.  This is done
766      * in order to choose among matches, if there are more than one.
767      */
768
769     /* Exact matches are the best. */
770     match = cm_NormStrCmp(matchName, sp->nsearchNamep);
771     if (match == 0) {
772         sp->ExactFound = 1;
773         cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
774         return CM_ERROR_STOPNOW;
775     }
776
777     /* Lower-case matches are next. */
778     if (sp->LCfound)
779         return 0;
780     if (cm_NoneUpper(matchName)) {
781         sp->LCfound = 1;
782         goto inexact;
783     }
784
785     /* Upper-case matches are next. */
786     if (sp->UCfound)
787         return 0;
788     if (cm_NoneLower(matchName)) {
789         sp->UCfound = 1;
790         goto inexact;
791     }
792
793     /* General matches are last. */
794     if (sp->NCfound)
795         return 0;
796     sp->NCfound = 1;
797
798   inexact:
799     cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
800     return 0;
801 }       
802
803 /* read the contents of a mount point into the appropriate string.
804  * called with write locked scp, and returns with locked scp.
805  */
806 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
807 {
808     long code;
809     cm_buf_t *bufp;
810     osi_hyper_t thyper;
811     int tlen;
812
813     if (scp->mountPointStringp[0]) 
814         return 0;
815         
816     /* otherwise, we have to read it in */
817     lock_ReleaseWrite(&scp->rw);
818
819     thyper.LowPart = thyper.HighPart = 0;
820     code = buf_Get(scp, &thyper, &bufp);
821
822     lock_ObtainWrite(&scp->rw);
823     if (code)
824         return code;
825
826     while (1) {
827         code = cm_SyncOp(scp, bufp, userp, reqp, 0,
828                           CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
829         if (code)
830             goto done;
831
832         cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
833
834         if (cm_HaveBuffer(scp, bufp, 0)) 
835             break;
836
837         /* otherwise load buffer */
838         code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
839         if (code)
840             goto done;
841     }
842     /* locked, has callback, has valid data in buffer */
843     if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1) 
844         return CM_ERROR_TOOBIG;
845     if (tlen <= 0) {
846         code = CM_ERROR_INVAL;
847         goto done;
848     }
849
850     /* someone else did the work while we were out */
851     if (scp->mountPointStringp[0]) {
852         code = 0;
853         goto done;
854     }
855
856     /* otherwise, copy out the link */
857     memcpy(scp->mountPointStringp, bufp->datap, tlen);
858
859     /* now make it null-terminated.  Note that the original contents of a
860      * link that is a mount point is "#volname." where "." is there just to
861      * be turned into a null.  That is, we can trash the last char of the
862      * link without damaging the vol name.  This is a stupid convention,
863      * but that's the protocol.
864      */
865     scp->mountPointStringp[tlen-1] = 0;
866     code = 0;
867
868   done:
869     if (bufp) 
870         buf_Release(bufp);
871     return code;
872 }
873
874
875 /* called with a locked scp and chases the mount point, yielding outScpp.
876  * scp remains write locked, just for simplicity of describing the interface.
877  */
878 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
879                          cm_req_t *reqp, cm_scache_t **outScpp)
880 {
881     fschar_t *cellNamep = NULL;
882     fschar_t *volNamep = NULL;
883     int tlen;
884     afs_uint32 code;
885     fschar_t *cp;
886     fschar_t *mpNamep;
887     cm_volume_t *volp = NULL;
888     cm_cell_t *cellp;
889     fschar_t mtType;
890     cm_fid_t tfid;
891     size_t vnLength;
892     int targetType;
893
894     if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
895         tfid = scp->mountRootFid;
896         lock_ReleaseWrite(&scp->rw);
897         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
898         lock_ObtainWrite(&scp->rw);
899         return code;
900     }
901
902     /* parse the volume name */
903     mpNamep = scp->mountPointStringp;
904     if (!mpNamep[0])
905         return CM_ERROR_NOSUCHPATH;
906     tlen = cm_FsStrLen(scp->mountPointStringp);
907     mtType = *scp->mountPointStringp;
908
909     cp = cm_FsStrChr(mpNamep, _FS(':'));
910     if (cp) {
911         /* cellular mount point */
912         cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
913         cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
914         volNamep = cm_FsStrDup(cp+1);
915
916         /* now look up the cell */
917         lock_ReleaseWrite(&scp->rw);
918         cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
919         lock_ObtainWrite(&scp->rw);
920     } else {
921         /* normal mt pt */
922         volNamep = cm_FsStrDup(mpNamep + 1);
923
924         cellp = cm_FindCellByID(scp->fid.cell, 0);
925     }
926
927     if (!cellp) {
928         code = CM_ERROR_NOSUCHCELL;
929         goto done;
930     }
931
932     vnLength = cm_FsStrLen(volNamep);
933     if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
934         targetType = BACKVOL;
935     else if (vnLength >= 10
936              && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
937         targetType = ROVOL;
938     else
939         targetType = RWVOL;
940
941     /* check for backups within backups */
942     if (targetType == BACKVOL
943          && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
944          == CM_SCACHEFLAG_RO) {
945         code = CM_ERROR_NOSUCHVOLUME;
946         goto done;
947     }
948
949     /* now we need to get the volume */
950     lock_ReleaseWrite(&scp->rw);
951     if (cm_VolNameIsID(volNamep)) {
952         code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp, 
953                                 CM_GETVOL_FLAG_CREATE, &volp);
954     } else {
955         code = cm_FindVolumeByName(cellp, volNamep, userp, reqp, 
956                                   CM_GETVOL_FLAG_CREATE, &volp);
957     }
958     lock_ObtainWrite(&scp->rw);
959         
960     if (code == 0) {
961         afs_uint32 cell, volume;
962         cm_vol_state_t *statep;
963
964         cell = cellp->cellID;
965         
966         /* if the mt pt originates in a .backup volume (not a .readonly)
967          * and FollowBackupPath is active, and if there is a .backup
968          * volume for the target, then use the .backup of the target
969          * instead of the read-write.
970          */
971         if (cm_followBackupPath && 
972             volp->vol[BACKVOL].ID != 0 &&
973             (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
974             (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
975             ) {
976             targetType = BACKVOL;
977         } 
978         /* if the mt pt is in a read-only volume (not just a
979          * backup), and if there is a read-only volume for the
980          * target, and if this is a targetType '#' mount point, use
981          * the read-only, otherwise use the one specified.
982          */
983         else if (mtType == '#' && targetType == RWVOL && 
984                  (scp->flags & CM_SCACHEFLAG_PURERO) && 
985                  volp->vol[ROVOL].ID != 0) {
986             targetType = ROVOL;
987         }
988
989         lock_ObtainWrite(&volp->rw);
990         statep = cm_VolumeStateByType(volp, targetType);
991         volume = statep->ID;
992         statep->dotdotFid = dscp->fid;
993         lock_ReleaseWrite(&volp->rw);
994
995         /* the rest of the fid is a magic number */
996         cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
997         scp->mountRootGen = cm_data.mountRootGen;
998
999         tfid = scp->mountRootFid;
1000         lock_ReleaseWrite(&scp->rw);
1001         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1002         lock_ObtainWrite(&scp->rw);
1003     }
1004
1005   done:
1006     if (volp)
1007         cm_PutVolume(volp);
1008     if (cellNamep)
1009         free(cellNamep);
1010     if (volNamep)
1011         free(volNamep);
1012     return code;
1013 }       
1014
1015 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1016                        cm_req_t *reqp, cm_scache_t **outpScpp)
1017 {
1018     long code;
1019     int dnlcHit = 1;    /* did we hit in the dnlc? yes, we did */
1020     cm_scache_t *tscp = NULL;
1021     cm_scache_t *mountedScp;
1022     cm_lookupSearch_t rock;
1023     int getroot;
1024     normchar_t *nnamep = NULL;
1025     fschar_t *fnamep = NULL;
1026
1027     memset(&rock, 0, sizeof(rock));
1028
1029     if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1030         && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1031         if (dscp->dotdotFid.volume == 0)
1032             return CM_ERROR_NOSUCHVOLUME;
1033         rock.fid = dscp->dotdotFid;
1034         goto haveFid;
1035     } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1036         rock.fid = dscp->fid;
1037         goto haveFid;
1038     }
1039
1040     nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1041     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1042
1043     if (flags & CM_FLAG_NOMOUNTCHASE) {
1044         /* In this case, we should go and call cm_Dir* functions
1045            directly since the following cm_ApplyDir() function will
1046            not. */
1047
1048         cm_dirOp_t dirop;
1049 #ifdef USE_BPLUS
1050         int usedBplus = 0;
1051 #endif
1052
1053         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1054         if (code == 0) {
1055 #ifdef USE_BPLUS
1056             code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1057             if (code != EINVAL)
1058                 usedBplus = 1;
1059             else
1060 #endif
1061                 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1062
1063             cm_EndDirOp(&dirop);
1064         }
1065
1066         if (code == 0) {
1067             /* found it */
1068             rock.found = TRUE;
1069             goto haveFid;
1070         }
1071 #ifdef USE_BPLUS
1072         if (usedBplus) {
1073             if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1074                 /* found it */
1075                 code = 0;
1076                 rock.found = TRUE;
1077                 goto haveFid;
1078             }
1079             
1080             return CM_ERROR_BPLUS_NOMATCH;
1081         }
1082 #endif
1083     }
1084
1085     rock.fid.cell = dscp->fid.cell;
1086     rock.fid.volume = dscp->fid.volume;
1087     rock.searchNamep = fnamep;
1088     rock.nsearchNamep = nnamep;
1089     rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1090     rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1091
1092     /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1093     code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1094                        (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1095
1096     /* code == 0 means we fell off the end of the dir, while stopnow means
1097      * that we stopped early, probably because we found the entry we're
1098      * looking for.  Any other non-zero code is an error.
1099      */
1100     if (code && code != CM_ERROR_STOPNOW) {
1101         /* if the cm_scache_t we are searching in is not a directory 
1102          * we must return path not found because the error 
1103          * is to describe the final component not an intermediary
1104          */
1105         if (code == CM_ERROR_NOTDIR) {
1106             if (flags & CM_FLAG_CHECKPATH)
1107                 code = CM_ERROR_NOSUCHPATH;
1108             else
1109                 code = CM_ERROR_NOSUCHFILE;
1110         }
1111         goto done;
1112     }
1113
1114     getroot = (dscp==cm_data.rootSCachep) ;
1115     if (!rock.found) {
1116         if (!cm_freelanceEnabled || !getroot) {
1117             if (flags & CM_FLAG_CHECKPATH)
1118                 code = CM_ERROR_NOSUCHPATH;
1119             else
1120                 code = CM_ERROR_NOSUCHFILE;
1121             goto done;
1122         }
1123         else if (!cm_ClientStrChr(cnamep, '#') &&
1124                  !cm_ClientStrChr(cnamep, '%') &&
1125                  cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1126                  cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1127                  cm_ClientStrCmpI(cnamep, _C("ipc$"))) 
1128         {
1129             /* nonexistent dir on freelance root, so add it */
1130             fschar_t fullname[200] = ".";
1131             int  found = 0;
1132
1133             osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S", 
1134                      osi_LogSaveClientString(afsd_logp,cnamep));
1135
1136             /* 
1137              * There is an ugly behavior where a share name "foo" will be searched
1138              * for as "fo".  If the searched for name differs by an already existing
1139              * symlink or mount point in the Freelance directory, do not add the 
1140              * new value automatically.
1141              */
1142
1143             code = -1;
1144             if (cnamep[0] == '.') {
1145                 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1146                     found = 1;
1147                     if (!cm_FreelanceMountPointExists(fullname, 0))
1148                         code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.",
1149                                                     1, &rock.fid);
1150                     if ( cm_FsStrCmpI(&fnamep[1], &fullname[1]) && 
1151                          !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1152                          !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1153                         code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1154                 }
1155             } else {
1156                 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1157                     found = 1;
1158                     if (!cm_FreelanceMountPointExists(fullname, 0))
1159                         code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
1160                     if ( cm_FsStrCmpI(fnamep, fullname) && 
1161                          !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1162                          !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1163                         code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1164                 }
1165             }
1166             if (!found || code < 0) {   /* add mount point failed, so give up */
1167                 if (flags & CM_FLAG_CHECKPATH)
1168                     code = CM_ERROR_NOSUCHPATH;
1169                 else
1170                     code = CM_ERROR_NOSUCHFILE;
1171                 goto done;
1172             }
1173             tscp = NULL;   /* to force call of cm_GetSCache */
1174         } else {
1175             if (flags & CM_FLAG_CHECKPATH)
1176                 code = CM_ERROR_NOSUCHPATH;
1177             else
1178                 code = CM_ERROR_NOSUCHFILE;
1179             goto done;
1180         }
1181     }
1182
1183   haveFid:       
1184     if ( !tscp )    /* we did not find it in the dnlc */
1185     {
1186         dnlcHit = 0; 
1187         code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1188         if (code) 
1189             goto done;
1190     }
1191     /* tscp is now held */
1192
1193     lock_ObtainWrite(&tscp->rw);
1194     code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1195                       CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1196     if (code) { 
1197         lock_ReleaseWrite(&tscp->rw);
1198         cm_ReleaseSCache(tscp);
1199         goto done;
1200     }
1201     cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1202     /* tscp is now locked */
1203
1204     if (!(flags & CM_FLAG_NOMOUNTCHASE)
1205          && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1206         /* mount points are funny: they have a volume name to mount
1207          * the root of.
1208          */
1209         code = cm_ReadMountPoint(tscp, userp, reqp);
1210         if (code == 0)
1211             code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1212                                        &mountedScp);
1213         lock_ReleaseWrite(&tscp->rw);
1214         cm_ReleaseSCache(tscp);
1215         if (code)
1216             goto done;
1217
1218         tscp = mountedScp;
1219     }
1220     else {
1221         lock_ReleaseWrite(&tscp->rw);
1222     }
1223
1224     /* copy back pointer */
1225     *outpScpp = tscp;
1226
1227     /* insert scache in dnlc */
1228     if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1229         /* lock the directory entry to prevent racing callback revokes */
1230         lock_ObtainRead(&dscp->rw);
1231         if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1232             /* TODO: reuse nnamep from above */
1233             if (nnamep) 
1234                 free(nnamep);
1235             nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1236             cm_dnlcEnter(dscp, nnamep, tscp);
1237         }
1238         lock_ReleaseRead(&dscp->rw);
1239     }
1240
1241     /* and return */
1242   done:
1243     if (fnamep) {
1244         free (fnamep);
1245         fnamep = NULL;
1246     }
1247     if (nnamep) {
1248         free (nnamep);
1249         nnamep = NULL;
1250     }
1251
1252     return code;
1253 }
1254
1255 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1256 {
1257     clientchar_t *tp;
1258     int prefixCount;
1259
1260     tp = cm_ClientStrRChr(inp, '@');
1261     if (tp == NULL) 
1262         return 0;               /* no @sys */
1263
1264     if (cm_ClientStrCmp(tp, _C("@sys")) != 0) 
1265         return 0;       /* no @sys */
1266
1267     /* caller just wants to know if this is a valid @sys type of name */
1268     if (outp == NULL) 
1269         return 1;
1270
1271     if (index >= cm_sysNameCount)
1272         return -1;
1273
1274     /* otherwise generate the properly expanded @sys name */
1275     prefixCount = (int)(tp - inp);
1276
1277     cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount);       /* copy out "a." from "a.@sys" */
1278     outp[prefixCount] = 0;              /* null terminate the "a." */
1279     cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1280     return 1;
1281 }   
1282
1283 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1284                                 cm_req_t *reqp, cm_scache_t ** outpScpp)
1285 {
1286     afs_uint32    code = 0;
1287     fschar_t      cellName[CELL_MAXNAMELEN];
1288     fschar_t      volumeName[VL_MAXNAMELEN];
1289     size_t        len;
1290     fschar_t *        cp;
1291     fschar_t *        tp;
1292     fschar_t *        fnamep = NULL;
1293
1294     cm_cell_t *   cellp = NULL;
1295     cm_volume_t * volp = NULL;
1296     cm_fid_t      fid;
1297     afs_uint32    volume;
1298     int           volType;
1299     int           mountType = RWVOL;
1300
1301     osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1302              osi_LogSaveClientString(afsd_logp, namep));
1303
1304     if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1305         goto _exit_invalid_path;
1306     }
1307
1308     /* namep is assumed to look like the following:
1309
1310        @vol:<cellname>%<volume>\0
1311        or
1312        @vol:<cellname>#<volume>\0
1313
1314      */
1315
1316     fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1317     cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1318     tp = cm_FsStrChr(cp, '%');
1319     if (tp == NULL)
1320         tp = cm_FsStrChr(cp, '#');
1321     if (tp == NULL ||
1322         (len = tp - cp) == 0 ||
1323         len > CELL_MAXNAMELEN)
1324         goto _exit_invalid_path;
1325     cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1326
1327     if (*tp == '#')
1328         mountType = ROVOL;
1329
1330     cp = tp+1;                  /* cp now points to volume, supposedly */
1331     cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1332
1333     /* OK, now we have the cell and the volume */
1334     osi_Log2(afsd_logp, "   Found cell [%s] and volume [%s]",
1335              osi_LogSaveFsString(afsd_logp, cellName),
1336              osi_LogSaveFsString(afsd_logp, volumeName));
1337
1338     cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1339     if (cellp == NULL) {
1340         goto _exit_invalid_path;
1341     }
1342
1343     len = cm_FsStrLen(volumeName);
1344     if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1345         volType = BACKVOL;
1346     else if (len >= 10 &&
1347              cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1348         volType = ROVOL;
1349     else
1350         volType = RWVOL;
1351
1352     if (cm_VolNameIsID(volumeName)) {
1353         code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1354                                 CM_GETVOL_FLAG_CREATE, &volp);
1355     } else {
1356         code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1357                                   CM_GETVOL_FLAG_CREATE, &volp);
1358     }
1359
1360     if (code != 0)
1361         goto _exit_cleanup;
1362
1363     if (volType == BACKVOL)
1364         volume = volp->vol[BACKVOL].ID;
1365     else if (volType == ROVOL ||
1366              (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1367         volume = volp->vol[ROVOL].ID;
1368     else
1369         volume = volp->vol[RWVOL].ID;
1370
1371     cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1372
1373     code = cm_GetSCache(&fid, outpScpp, userp, reqp);
1374
1375   _exit_cleanup:
1376     if (fnamep)
1377         free(fnamep);
1378
1379     if (volp)
1380         cm_PutVolume(volp);
1381
1382     if (code == 0)
1383         return code;
1384
1385  _exit_invalid_path:
1386     if (flags & CM_FLAG_CHECKPATH)
1387         return CM_ERROR_NOSUCHPATH;
1388     else
1389         return CM_ERROR_NOSUCHFILE;
1390 }
1391
1392 #ifdef DEBUG_REFCOUNT
1393 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1394                cm_req_t *reqp, cm_scache_t **outpScpp, char * file, long line)
1395 #else
1396 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1397                cm_req_t *reqp, cm_scache_t **outpScpp)
1398 #endif
1399 {
1400     long code;
1401     clientchar_t tname[AFSPATHMAX];
1402     int sysNameIndex = 0;
1403     cm_scache_t *scp = NULL;
1404
1405 #ifdef DEBUG_REFCOUNT
1406     afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1407     osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1408 #endif
1409
1410     if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1411         if (flags & CM_FLAG_CHECKPATH)
1412             return CM_ERROR_NOSUCHPATH;
1413         else
1414             return CM_ERROR_NOSUCHFILE;
1415     }
1416
1417     if (dscp == cm_data.rootSCachep &&
1418         cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1419         return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outpScpp);
1420     }
1421
1422     if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1423         for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1424             code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1425             if (code > 0) {
1426                 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1427 #ifdef DEBUG_REFCOUNT
1428                 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);
1429                 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1430 #endif
1431
1432                 if (code == 0) {
1433                     *outpScpp = scp;
1434                     return 0;
1435                 }
1436                 if (scp) {
1437                     cm_ReleaseSCache(scp);
1438                     scp = NULL;
1439                 }
1440             } else {
1441                 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1442 #ifdef DEBUG_REFCOUNT
1443                 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);
1444                 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1445 #endif
1446                 *outpScpp = scp;
1447                 return code;
1448             }
1449         }
1450     } else {
1451         code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1452 #ifdef DEBUG_REFCOUNT
1453         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);
1454         osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1455 #endif
1456         *outpScpp = scp;
1457         return code;
1458     }
1459
1460     /* None of the possible sysName expansions could be found */
1461     if (flags & CM_FLAG_CHECKPATH)
1462         return CM_ERROR_NOSUCHPATH;
1463     else
1464         return CM_ERROR_NOSUCHFILE;
1465 }
1466
1467 /*! \brief Unlink a file name
1468
1469   Encapsulates a call to RXAFS_RemoveFile().
1470
1471   \param[in] dscp cm_scache_t pointing at the directory containing the
1472       name to be unlinked.
1473
1474   \param[in] fnamep Original name to be unlinked.  This is the
1475       name that will be passed into the RXAFS_RemoveFile() call.
1476       This parameter is optional.  If not provided, the value will
1477       be looked up.
1478
1479   \param[in] came Client name to be unlinked.  This name will be used
1480       to update the local directory caches.
1481
1482   \param[in] userp cm_user_t for the request.
1483
1484   \param[in] reqp Request tracker.
1485  
1486  */
1487 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1488                cm_user_t *userp, cm_req_t *reqp)
1489 {
1490     long code;
1491     cm_conn_t *connp;
1492     AFSFid afsFid;
1493     int sflags;
1494     AFSFetchStatus newDirStatus;
1495     AFSVolSync volSync;
1496     struct rx_connection * rxconnp;
1497     cm_dirOp_t dirop;
1498     cm_scache_t *scp = NULL;
1499     int free_fnamep = FALSE;
1500
1501     if (fnamep == NULL) {
1502         code = -1;
1503 #ifdef USE_BPLUS
1504         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1505         if (code == 0) {
1506             code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1507             if (code == 0)
1508                 free_fnamep = TRUE;
1509             cm_EndDirOp(&dirop);
1510         }
1511 #endif
1512         if (code)
1513             goto done;
1514     }
1515
1516 #ifdef AFS_FREELANCE_CLIENT
1517     if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1518         /* deleting a mount point from the root dir. */
1519         code = cm_FreelanceRemoveMount(fnamep);
1520         goto done;
1521     }
1522 #endif  
1523
1524     code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1525
1526     /* make sure we don't screw up the dir status during the merge */
1527     code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
1528
1529     lock_ObtainWrite(&dscp->rw);
1530     sflags = CM_SCACHESYNC_STOREDATA;
1531     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1532     lock_ReleaseWrite(&dscp->rw);
1533     if (code) {
1534         cm_EndDirOp(&dirop);
1535         goto done;
1536     }
1537
1538     /* make the RPC */
1539     afsFid.Volume = dscp->fid.volume;
1540     afsFid.Vnode = dscp->fid.vnode;
1541     afsFid.Unique = dscp->fid.unique;
1542
1543     osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1544     do {
1545         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1546         if (code)
1547             continue;
1548
1549         rxconnp = cm_GetRxConn(connp);
1550         code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1551                                 &newDirStatus, &volSync);
1552         rx_PutConnection(rxconnp);
1553
1554     } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1555     code = cm_MapRPCError(code, reqp);
1556
1557     if (code)
1558         osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1559     else
1560         osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1561
1562     if (dirop.scp) {
1563         lock_ObtainWrite(&dirop.scp->dirlock);
1564         dirop.lockType = CM_DIRLOCK_WRITE;
1565     }
1566     lock_ObtainWrite(&dscp->rw);
1567     cm_dnlcRemove(dscp, cnamep);
1568     cm_SyncOpDone(dscp, NULL, sflags);
1569     if (code == 0) {
1570         cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
1571     } else if (code == CM_ERROR_NOSUCHFILE) {
1572         /* windows would not have allowed the request to delete the file 
1573          * if it did not believe the file existed.  therefore, we must 
1574          * have an inconsistent view of the world.
1575          */
1576         dscp->cbServerp = NULL;
1577     }
1578     lock_ReleaseWrite(&dscp->rw);
1579
1580     if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1581         cm_DirDeleteEntry(&dirop, fnamep);
1582 #ifdef USE_BPLUS
1583         cm_BPlusDirDeleteEntry(&dirop, cnamep);
1584 #endif
1585     }
1586     cm_EndDirOp(&dirop);
1587
1588     if (scp) {
1589         cm_ReleaseSCache(scp);
1590         if (code == 0) {
1591             lock_ObtainWrite(&scp->rw);
1592             scp->flags |= CM_SCACHEFLAG_DELETED;
1593             lock_ReleaseWrite(&scp->rw);
1594         }
1595     }
1596
1597   done:
1598     if (free_fnamep)
1599         free(fnamep);
1600
1601     return code;
1602 }
1603
1604 /* called with a write locked vnode, and fills in the link info.
1605  * returns this the vnode still write locked.
1606  */
1607 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1608 {
1609     long code;
1610     cm_buf_t *bufp;
1611     long temp;
1612     osi_hyper_t thyper;
1613
1614     lock_AssertWrite(&linkScp->rw);
1615     if (!linkScp->mountPointStringp[0]) {
1616         /* read the link data */
1617         lock_ReleaseWrite(&linkScp->rw);
1618         thyper.LowPart = thyper.HighPart = 0;
1619         code = buf_Get(linkScp, &thyper, &bufp);
1620         lock_ObtainWrite(&linkScp->rw);
1621         if (code) 
1622             return code;
1623         while (1) {
1624             code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1625                               CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1626             if (code) {
1627                 buf_Release(bufp);
1628                 return code;
1629             }
1630             cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1631
1632             if (cm_HaveBuffer(linkScp, bufp, 0)) 
1633                 break;
1634
1635             code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1636             if (code) {
1637                 buf_Release(bufp);
1638                 return code;
1639             }
1640         } /* while loop to get the data */
1641                 
1642         /* now if we still have no link read in,
1643          * copy the data from the buffer */
1644         if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1645             buf_Release(bufp);
1646             return CM_ERROR_TOOBIG;
1647         }
1648
1649         /* otherwise, it fits; make sure it is still null (could have
1650          * lost race with someone else referencing this link above),
1651          * and if so, copy in the data.
1652          */
1653         if (!linkScp->mountPointStringp[0]) {
1654             strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1655             linkScp->mountPointStringp[temp] = 0;       /* null terminate */
1656
1657             if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1658                  linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1659         }
1660         buf_Release(bufp);
1661     }   /* don't have sym link contents cached */
1662
1663     return 0;
1664 }       
1665
1666 /* called with a held vnode and a path suffix, with the held vnode being a
1667  * symbolic link.  Our goal is to generate a new path to interpret, and return
1668  * this new path in newSpaceBufferp.  If the new vnode is relative to a dir
1669  * other than the directory containing the symbolic link, then the new root is
1670  * returned in *newRootScpp, otherwise a null is returned there.
1671  */
1672 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1673                      cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1674                      cm_user_t *userp, cm_req_t *reqp)
1675 {
1676     long code = 0;
1677     long len;
1678     fschar_t *linkp;
1679     cm_space_t *tsp;
1680
1681     *newRootScpp = NULL;
1682     *newSpaceBufferp = NULL;
1683
1684     lock_ObtainWrite(&linkScp->rw);
1685     code = cm_HandleLink(linkScp, userp, reqp);
1686     if (code)
1687         goto done;
1688
1689     /* if we may overflow the buffer, bail out; buffer is signficantly
1690      * bigger than max path length, so we don't really have to worry about
1691      * being a little conservative here.
1692      */
1693     if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1694         >= CM_UTILS_SPACESIZE) {
1695         code = CM_ERROR_TOOBIG;
1696         goto done;
1697     }
1698
1699     tsp = cm_GetSpace();
1700     linkp = linkScp->mountPointStringp;
1701     if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1702         if (strlen(linkp) > cm_mountRootLen)
1703             StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1704         else
1705             tsp->data[0] = 0;
1706         *newRootScpp = cm_data.rootSCachep;
1707         cm_HoldSCache(cm_data.rootSCachep);
1708     } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1709         if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName)))) 
1710         {
1711             char * p = &linkp[len + 3];
1712             if (strnicmp(p, "all", 3) == 0)
1713                 p += 4;
1714
1715             StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1716             for (p = tsp->data; *p; p++) {
1717                 if (*p == '\\')
1718                     *p = '/';
1719             }
1720             *newRootScpp = cm_data.rootSCachep;
1721             cm_HoldSCache(cm_data.rootSCachep);
1722         } else {
1723             linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1724             StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1725             code = CM_ERROR_PATH_NOT_COVERED;
1726         }
1727     } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1728                 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1729         linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1730         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1731         code = CM_ERROR_PATH_NOT_COVERED;
1732     } else if (*linkp == '\\' || *linkp == '/') {
1733 #if 0   
1734         /* formerly, this was considered to be from the AFS root,
1735          * but this seems to create problems.  instead, we will just
1736          * reject the link */
1737         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1738         *newRootScpp = cm_data.rootSCachep;
1739         cm_HoldSCache(cm_data.rootSCachep);
1740 #else
1741         /* we still copy the link data into the response so that 
1742          * the user can see what the link points to
1743          */
1744         linkScp->fileType = CM_SCACHETYPE_INVALID;
1745         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1746         code = CM_ERROR_NOSUCHPATH;
1747 #endif  
1748     } else {
1749         /* a relative link */
1750         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1751     }
1752     if (pathSuffixp[0] != 0) {  /* if suffix string is non-null */
1753         StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1754         StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1755     }
1756     if (code == 0) {
1757         clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1758         cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1759         free(cpath);
1760         *newSpaceBufferp = tsp;
1761     } else {
1762         cm_FreeSpace(tsp);
1763
1764         if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1765             cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1766         }
1767     }
1768
1769  done:
1770     lock_ReleaseWrite(&linkScp->rw);
1771     return code;
1772 }
1773 #ifdef DEBUG_REFCOUNT
1774 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1775                  cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1776                  cm_scache_t **outScpp, 
1777                  char * file, long line)
1778 #else
1779 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1780               cm_user_t *userp, clientchar_t *tidPathp,
1781               cm_req_t *reqp, cm_scache_t **outScpp)
1782 #endif
1783 {
1784     long code;
1785     clientchar_t *tp;                   /* ptr moving through input buffer */
1786     clientchar_t tc;                    /* temp char */
1787     int haveComponent;          /* has new component started? */
1788     clientchar_t component[AFSPATHMAX]; /* this is the new component */
1789     clientchar_t *cp;                   /* component name being assembled */
1790     cm_scache_t *tscp;          /* current location in the hierarchy */
1791     cm_scache_t *nscp;          /* next dude down */
1792     cm_scache_t *dirScp;        /* last dir we searched */
1793     cm_scache_t *linkScp;       /* new root for the symlink we just
1794     * looked up */
1795     cm_space_t *psp;            /* space for current path, if we've hit
1796     * any symlinks */
1797     cm_space_t *tempsp;         /* temp vbl */
1798     clientchar_t *restp;                /* rest of the pathname to interpret */
1799     int symlinkCount;           /* count of # of symlinks traversed */
1800     int extraFlag;              /* avoid chasing mt pts for dir cmd */
1801     int phase = 1;              /* 1 = tidPathp, 2 = pathp */
1802 #define MAX_FID_COUNT 512
1803     cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1804     int fid_count = 0;          /* number of fids processed in this path walk */
1805     int i;
1806
1807 #ifdef DEBUG_REFCOUNT
1808     afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1809     osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1810              rootSCachep, pathp ? pathp : "<NULL>", tidPathp ? tidPathp : "<NULL>", 
1811              flags);
1812 #endif
1813
1814     tp = tidPathp;
1815     if (tp == NULL) {
1816         tp = pathp;
1817         phase = 2;
1818     }
1819     if (tp == NULL) {
1820         tp = _C("");
1821     }
1822     haveComponent = 0;
1823     psp = NULL;
1824     tscp = rootSCachep;
1825     cm_HoldSCache(tscp);
1826     symlinkCount = 0;
1827     dirScp = NULL;
1828
1829
1830     while (1) {
1831         tc = *tp++;
1832
1833         /* map Unix slashes into DOS ones so we can interpret Unix
1834          * symlinks properly
1835          */
1836         if (tc == '/') 
1837             tc = '\\';
1838
1839         if (!haveComponent) {
1840             if (tc == '\\') {
1841                 continue;
1842             } else if (tc == 0) {
1843                 if (phase == 1) {
1844                     phase = 2;
1845                     tp = pathp;
1846                     continue;
1847                 }
1848                 code = 0;
1849                 break;
1850             } else {
1851                 haveComponent = 1;
1852                 cp = component;
1853                 *cp++ = tc;
1854             }
1855         } else {
1856             /* we have a component here */
1857             if (tc == 0 || tc == '\\') {
1858                 /* end of the component; we're at the last
1859                  * component if tc == 0.  However, if the last
1860                  * is a symlink, we have more to do.
1861                  */
1862                 *cp++ = 0;      /* add null termination */
1863                 extraFlag = 0;
1864                 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1865                     extraFlag = CM_FLAG_NOMOUNTCHASE;
1866                 code = cm_Lookup(tscp, component,
1867                                  flags | extraFlag,
1868                                  userp, reqp, &nscp);
1869
1870                 if (code == 0) {
1871                     if (!cm_ClientStrCmp(component,_C("..")) ||
1872                         !cm_ClientStrCmp(component,_C("."))) {
1873                         /* 
1874                          * roll back the fid list until we find the
1875                          * fid that matches where we are now.  Its not
1876                          * necessarily one or two fids because they
1877                          * might have been symlinks or mount points or
1878                          * both that were crossed.
1879                          */
1880                         for ( i=fid_count-1; i>=0; i--) {
1881                             if (!cm_FidCmp(&nscp->fid, &fids[i]))
1882                                 break;
1883                         }
1884                         fid_count = i+1;
1885                     } else {
1886                         /* add the new fid to the list */
1887                         for ( i=0; i<fid_count; i++) {
1888                             if ( !cm_FidCmp(&nscp->fid, &fids[i]) ) {
1889                                 code = CM_ERROR_TOO_MANY_SYMLINKS;
1890                                 cm_ReleaseSCache(nscp);
1891                                 nscp = NULL;
1892                                 break;
1893                             }
1894                         }
1895                         if (i == fid_count && fid_count < MAX_FID_COUNT) {
1896                             fids[fid_count++] = nscp->fid;
1897                         }
1898                     }
1899                 }
1900
1901                 if (code) {
1902                     cm_ReleaseSCache(tscp);
1903                     if (dirScp)
1904                         cm_ReleaseSCache(dirScp);
1905                     if (psp) 
1906                         cm_FreeSpace(psp);
1907                     if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) && 
1908                         tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1909                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1910                         return CM_ERROR_NOSUCHPATH;
1911                     } else {
1912                         osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1913                         return code;
1914                     }
1915                 }
1916
1917                 haveComponent = 0;      /* component done */
1918                 if (dirScp)
1919                     cm_ReleaseSCache(dirScp);
1920                 dirScp = tscp;          /* for some symlinks */
1921                 tscp = nscp;            /* already held */
1922                 nscp = NULL;
1923                 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1924                     code = 0;
1925                     if (dirScp) {
1926                         cm_ReleaseSCache(dirScp);
1927                         dirScp = NULL;
1928                     }
1929                     break;
1930                 }
1931
1932                 /* now, if tscp is a symlink, we should follow it and
1933                  * assemble the path again.
1934                  */
1935                 lock_ObtainWrite(&tscp->rw);
1936                 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1937                                   CM_SCACHESYNC_GETSTATUS
1938                                   | CM_SCACHESYNC_NEEDCALLBACK);
1939                 if (code) {
1940                     lock_ReleaseWrite(&tscp->rw);
1941                     cm_ReleaseSCache(tscp);
1942                     tscp = NULL;
1943                     if (dirScp) {
1944                         cm_ReleaseSCache(dirScp);
1945                         dirScp = NULL;
1946                     }
1947                     break;
1948                 }
1949                 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1950
1951                 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1952                     /* this is a symlink; assemble a new buffer */
1953                     lock_ReleaseWrite(&tscp->rw);
1954                     if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1955                         cm_ReleaseSCache(tscp);
1956                         tscp = NULL;
1957                         if (dirScp) {
1958                             cm_ReleaseSCache(dirScp);
1959                             dirScp = NULL;
1960                         }
1961                         if (psp) 
1962                             cm_FreeSpace(psp);
1963                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
1964                         return CM_ERROR_TOO_MANY_SYMLINKS;
1965                     }
1966                     if (tc == 0) 
1967                         restp = _C("");
1968                     else 
1969                         restp = tp;
1970
1971                     {
1972                         fschar_t * frestp;
1973
1974                         /* TODO: make this better */
1975                         frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
1976                         code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
1977                         free(frestp);
1978                     }
1979
1980                     if (code == 0 && linkScp != NULL) {
1981                         if (linkScp == cm_data.rootSCachep) 
1982                             fid_count = 0;
1983                         else {
1984                             for ( i=0; i<fid_count; i++) {
1985                                 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
1986                                     code = CM_ERROR_TOO_MANY_SYMLINKS;
1987                                     cm_ReleaseSCache(linkScp);
1988                                     nscp = NULL;
1989                                     break;
1990                                 }
1991                             }
1992                         }
1993                         if (i == fid_count && fid_count < MAX_FID_COUNT) {
1994                             fids[fid_count++] = linkScp->fid;
1995                         }
1996                     }
1997
1998                     if (code) {
1999                         /* something went wrong */
2000                         cm_ReleaseSCache(tscp);
2001                         tscp = NULL;
2002                         if (dirScp) {
2003                             cm_ReleaseSCache(dirScp);
2004                             dirScp = NULL;
2005                         }
2006                         break;
2007                     }
2008
2009                     /* otherwise, tempsp has the new path,
2010                      * and linkScp is the new root from
2011                      * which to interpret that path.
2012                      * Continue with the namei processing,
2013                      * also doing the bookkeeping for the
2014                      * space allocation and tracking the
2015                      * vnode reference counts.
2016                      */
2017                     if (psp) 
2018                         cm_FreeSpace(psp);
2019                     psp = tempsp;
2020                     tp = psp->wdata;
2021                     cm_ReleaseSCache(tscp);
2022                     tscp = linkScp;
2023                     linkScp = NULL;
2024                     /* already held
2025                      * by AssembleLink
2026                      * now, if linkScp is null, that's
2027                      * AssembleLink's way of telling us that
2028                      * the sym link is relative to the dir
2029                      * containing the link.  We have a ref
2030                      * to it in dirScp, and we hold it now
2031                      * and reuse it as the new spot in the
2032                      * dir hierarchy.
2033                      */
2034                     if (tscp == NULL) {
2035                         tscp = dirScp;
2036                         dirScp = NULL;
2037                     }
2038                 } else {
2039                     /* not a symlink, we may be done */
2040                     lock_ReleaseWrite(&tscp->rw);
2041                     if (tc == 0) {
2042                         if (phase == 1) {
2043                             phase = 2;
2044                             tp = pathp;
2045                             continue;
2046                         }
2047                         if (dirScp) {
2048                             cm_ReleaseSCache(dirScp);
2049                             dirScp = NULL;
2050                         }
2051                         code = 0;
2052                         break;
2053                     }
2054                 }
2055                 if (dirScp) {
2056                     cm_ReleaseSCache(dirScp);
2057                     dirScp = NULL;
2058                 }
2059             } /* end of a component */
2060             else 
2061                 *cp++ = tc;
2062         } /* we have a component */
2063     } /* big while loop over all components */
2064
2065     /* already held */
2066     if (dirScp)
2067         cm_ReleaseSCache(dirScp);
2068     if (psp) 
2069         cm_FreeSpace(psp);
2070     if (code == 0) 
2071         *outScpp = tscp;
2072     else if (tscp)
2073         cm_ReleaseSCache(tscp);
2074
2075 #ifdef DEBUG_REFCOUNT
2076     afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp)->refCount);
2077 #endif
2078     osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2079     return code;
2080 }
2081
2082 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2083  * We chase the link, and return a held pointer to the target, if it exists,
2084  * in *outScpp.  If we succeed, we return 0, otherwise we return an error code
2085  * and do not hold or return a target vnode.
2086  *
2087  * This is very similar to calling cm_NameI with the last component of a name,
2088  * which happens to be a symlink, except that we've already passed by the name.
2089  *
2090  * This function is typically called by the directory listing functions, which
2091  * encounter symlinks but need to return the proper file length so programs
2092  * like "more" work properly when they make use of the attributes retrieved from
2093  * the dir listing.
2094  *
2095  * The input vnode should not be locked when this function is called.
2096  */
2097 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2098                          cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2099 {
2100     long code;
2101     cm_space_t *spacep;
2102     cm_scache_t *newRootScp;
2103
2104     osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2105
2106     code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2107     if (code) 
2108         return code;
2109
2110     /* now, if newRootScp is NULL, we're really being told that the symlink
2111      * is relative to the current directory (dscp).
2112      */
2113     if (newRootScp == NULL) {
2114         newRootScp = dscp;
2115         cm_HoldSCache(dscp);
2116     }
2117
2118     code = cm_NameI(newRootScp, spacep->wdata,
2119                     CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2120                     userp, NULL, reqp, outScpp);
2121
2122     if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2123         code = CM_ERROR_NOSUCHPATH;
2124
2125     /* this stuff is allocated no matter what happened on the namei call,
2126      * so free it */
2127     cm_FreeSpace(spacep);
2128     cm_ReleaseSCache(newRootScp);
2129
2130     if (linkScp == *outScpp) {
2131         cm_ReleaseSCache(*outScpp);
2132         *outScpp = NULL;
2133         code = CM_ERROR_NOSUCHPATH;
2134     }
2135
2136     return code;
2137 }
2138
2139 /* for a given entry, make sure that it isn't in the stat cache, and then
2140  * add it to the list of file IDs to be obtained.
2141  *
2142  * Don't bother adding it if we already have a vnode.  Note that the dir
2143  * is locked, so we have to be careful checking the vnode we're thinking of
2144  * processing, to avoid deadlocks.
2145  */
2146 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2147                      osi_hyper_t *offp)
2148 {
2149     osi_hyper_t thyper;
2150     cm_bulkStat_t *bsp;
2151     int i;
2152     cm_scache_t *tscp;
2153     cm_fid_t tfid;
2154
2155     bsp = rockp;
2156
2157     /* Don't overflow bsp. */
2158     if (bsp->counter >= CM_BULKMAX)
2159         return CM_ERROR_STOPNOW;
2160
2161     thyper.LowPart = cm_data.buf_blockSize;
2162     thyper.HighPart = 0;
2163     thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2164
2165     /* thyper is now the first byte past the end of the record we're
2166      * interested in, and bsp->bufOffset is the first byte of the record
2167      * we're interested in.
2168      * Skip data in the others.
2169      * Skip '.' and '..'
2170      */
2171     if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2172         return 0;
2173     if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2174         return CM_ERROR_STOPNOW;
2175     if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2176         return 0;
2177
2178     cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2179     tscp = cm_FindSCache(&tfid);
2180     if (tscp) {
2181         if (lock_TryWrite(&tscp->rw)) {
2182             /* we have an entry that we can look at */
2183             if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2184                 /* we have a callback on it.  Don't bother
2185                  * fetching this stat entry, since we're happy
2186                  * with the info we have.
2187                  */
2188                 lock_ReleaseWrite(&tscp->rw);
2189                 cm_ReleaseSCache(tscp);
2190                 return 0;
2191             }
2192             lock_ReleaseWrite(&tscp->rw);
2193         }       /* got lock */
2194         cm_ReleaseSCache(tscp);
2195     }   /* found entry */
2196
2197 #ifdef AFS_FREELANCE_CLIENT
2198     // yj: if this is a mountpoint under root.afs then we don't want it
2199     // to be bulkstat-ed, instead, we call getSCache directly and under
2200     // getSCache, it is handled specially.
2201     if  ( cm_freelanceEnabled &&
2202           tfid.cell==AFS_FAKE_ROOT_CELL_ID && 
2203           tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2204           !(tfid.vnode==0x1 && tfid.unique==0x1) )
2205     {       
2206         osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2207         return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2208     }
2209 #endif /* AFS_FREELANCE_CLIENT */
2210
2211     i = bsp->counter++;
2212     bsp->fids[i].Volume = scp->fid.volume;
2213     bsp->fids[i].Vnode = tfid.vnode;
2214     bsp->fids[i].Unique = tfid.unique;
2215     return 0;
2216 }       
2217
2218 afs_int32
2219 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2220 {
2221     afs_int32 code = 0;
2222     AFSCBFids fidStruct;
2223     AFSBulkStats statStruct;
2224     cm_conn_t *connp;
2225     AFSCBs callbackStruct;
2226     long filex;
2227     AFSVolSync volSync;
2228     cm_callbackRequest_t cbReq;
2229     long filesThisCall;
2230     long i;
2231     long j;
2232     cm_scache_t *scp;
2233     cm_fid_t tfid;
2234     struct rx_connection * rxconnp;
2235     int inlinebulk = 0;         /* Did we use InlineBulkStatus RPC or not? */
2236         
2237     /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2238      * make the calls to create the entries.  Handle AFSCBMAX files at a
2239      * time.
2240      */
2241     for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2242         filesThisCall = bbp->counter - filex;
2243         if (filesThisCall > AFSCBMAX) 
2244             filesThisCall = AFSCBMAX;
2245
2246         fidStruct.AFSCBFids_len = filesThisCall;
2247         fidStruct.AFSCBFids_val = &bbp->fids[filex];
2248         statStruct.AFSBulkStats_len = filesThisCall;
2249         statStruct.AFSBulkStats_val = &bbp->stats[filex];
2250         callbackStruct.AFSCBs_len = filesThisCall;
2251         callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2252         cm_StartCallbackGrantingCall(NULL, &cbReq);
2253         osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2254         do {
2255             code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2256             if (code) 
2257                 continue;
2258
2259             rxconnp = cm_GetRxConn(connp);
2260             if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2261                 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2262                                      &statStruct, &callbackStruct, &volSync);
2263                 if (code == RXGEN_OPCODE) {
2264                     cm_SetServerNoInlineBulk(connp->serverp, 0);
2265                 } else {
2266                     inlinebulk = 1;
2267                 }
2268             }
2269             if (!inlinebulk) {
2270                 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2271                                         &statStruct, &callbackStruct, &volSync);
2272             }
2273             rx_PutConnection(rxconnp);
2274
2275         } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2276                              &volSync, NULL, &cbReq, code));
2277         code = cm_MapRPCError(code, reqp);
2278         if (code)
2279             osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x", 
2280                       inlinebulk ? "Inline" : "", code);
2281         else
2282             osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2283
2284         /* may as well quit on an error, since we're not going to do
2285          * much better on the next immediate call, either.
2286          */
2287         if (code) {
2288             cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2289             break;
2290         }
2291
2292         /* otherwise, we should do the merges */
2293         for (i = 0; i<filesThisCall; i++) {
2294             j = filex + i;
2295             cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2296             code = cm_GetSCache(&tfid, &scp, userp, reqp);
2297             if (code != 0) 
2298                 continue;
2299
2300             /* otherwise, if this entry has no callback info, 
2301              * merge in this.
2302              */
2303             lock_ObtainWrite(&scp->rw);
2304             /* now, we have to be extra paranoid on merging in this
2305              * information, since we didn't use cm_SyncOp before
2306              * starting the fetch to make sure that no bad races
2307              * were occurring.  Specifically, we need to make sure
2308              * we don't obliterate any newer information in the
2309              * vnode than have here.
2310              *
2311              * Right now, be pretty conservative: if there's a
2312              * callback or a pending call, skip it.
2313              */
2314             if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2315                  && !(scp->flags &
2316                        (CM_SCACHEFLAG_FETCHING
2317                          | CM_SCACHEFLAG_STORING
2318                          | CM_SCACHEFLAG_SIZESTORING))) {
2319                 cm_EndCallbackGrantingCall(scp, &cbReq,
2320                                             &bbp->callbacks[j],
2321                                             CM_CALLBACK_MAINTAINCOUNT);
2322                 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, 0);
2323             }       
2324             lock_ReleaseWrite(&scp->rw);
2325             cm_ReleaseSCache(scp);
2326         } /* all files in the response */
2327         /* now tell it to drop the count,
2328          * after doing the vnode processing above */
2329         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2330     }   /* while there are still more files to process */
2331
2332     /* If we did the InlineBulk RPC pull out the return code and log it */
2333     if (inlinebulk) {
2334         if ((&bbp->stats[0])->errorCode) {
2335             osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d", 
2336                      (&bbp->stats[0])->errorCode);
2337             code = (&bbp->stats[0])->errorCode;
2338         }
2339     }
2340
2341     return code;
2342 }
2343
2344 /* called with a write locked scp and a pointer to a buffer.  Make bulk stat
2345  * calls on all undeleted files in the page of the directory specified.
2346  */
2347 afs_int32
2348 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2349                cm_req_t *reqp)
2350 {
2351     afs_int32 code;
2352     cm_bulkStat_t *bbp;
2353
2354     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2355
2356     /* should be on a buffer boundary */
2357     osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2358
2359     bbp = malloc(sizeof(cm_bulkStat_t));
2360     memset(bbp, 0, sizeof(cm_bulkStat_t));
2361     bbp->bufOffset = *offsetp;
2362
2363     lock_ReleaseWrite(&dscp->rw);
2364     /* first, assemble the file IDs we need to stat */
2365     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2366
2367     /* if we failed, bail out early */
2368     if (code && code != CM_ERROR_STOPNOW) {
2369         free(bbp);
2370         lock_ObtainWrite(&dscp->rw);
2371         return code;
2372     }
2373
2374     code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2375     osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2376
2377     lock_ObtainWrite(&dscp->rw);
2378     free(bbp);
2379     return 0;
2380 }       
2381
2382 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2383 {
2384     long mask;
2385
2386     /* initialize store back mask as inexpensive local variable */
2387     mask = 0;
2388     memset(statusp, 0, sizeof(AFSStoreStatus));
2389
2390     /* copy out queued info from scache first, if scp passed in */
2391     if (scp) {
2392         if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2393             statusp->ClientModTime = scp->clientModTime;
2394             mask |= AFS_SETMODTIME;
2395             scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2396         }
2397     }
2398
2399     if (attrp) {
2400         /* now add in our locally generated request */
2401         if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2402             statusp->ClientModTime = attrp->clientModTime;
2403             mask |= AFS_SETMODTIME;
2404         }
2405         if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2406             statusp->UnixModeBits = attrp->unixModeBits;
2407             mask |= AFS_SETMODE;
2408         }
2409         if (attrp->mask & CM_ATTRMASK_OWNER) {
2410             statusp->Owner = attrp->owner;
2411             mask |= AFS_SETOWNER;
2412         }
2413         if (attrp->mask & CM_ATTRMASK_GROUP) {
2414             statusp->Group = attrp->group;
2415             mask |= AFS_SETGROUP;
2416         }
2417     }
2418     statusp->Mask = mask;
2419 }       
2420
2421 /* set the file size, and make sure that all relevant buffers have been
2422  * truncated.  Ensure that any partially truncated buffers have been zeroed
2423  * to the end of the buffer.
2424  */
2425 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2426                    cm_req_t *reqp)
2427 {
2428     long code;
2429     int shrinking;
2430
2431     /* start by locking out buffer creation */
2432     lock_ObtainWrite(&scp->bufCreateLock);
2433
2434     /* verify that this is a file, not a dir or a symlink */
2435     lock_ObtainWrite(&scp->rw);
2436     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2437                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2438     if (code) 
2439         goto done;
2440     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2441
2442     if (scp->fileType != CM_SCACHETYPE_FILE) {
2443         code = CM_ERROR_ISDIR;
2444         goto done;
2445     }
2446
2447   startover:
2448     if (LargeIntegerLessThan(*sizep, scp->length))
2449         shrinking = 1;
2450     else
2451         shrinking = 0;
2452
2453     lock_ReleaseWrite(&scp->rw);
2454
2455     /* can't hold scp->rw lock here, since we may wait for a storeback to
2456      * finish if the buffer package is cleaning a buffer by storing it to
2457      * the server.
2458      */
2459     if (shrinking)
2460         buf_Truncate(scp, userp, reqp, sizep);
2461
2462     /* now ensure that file length is short enough, and update truncPos */
2463     lock_ObtainWrite(&scp->rw);
2464
2465     /* make sure we have a callback (so we have the right value for the
2466      * length), and wait for it to be safe to do a truncate.
2467      */
2468     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2469                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2470                       | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2471
2472     /* If we only have 'i' bits, then we should still be able to set
2473        the size of a file we created. */
2474     if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2475         code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2476                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2477                          | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2478     }
2479
2480     if (code) 
2481         goto done;
2482
2483     if (LargeIntegerLessThan(*sizep, scp->length)) {
2484         /* a real truncation.  If truncPos is not set yet, or is bigger
2485          * than where we're truncating the file, set truncPos to this
2486          * new value.
2487          */
2488         if (!shrinking)
2489             goto startover;
2490         if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2491              || LargeIntegerLessThan(*sizep, scp->length)) {
2492             /* set trunc pos */
2493             scp->truncPos = *sizep;
2494             scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2495         }
2496         /* in either case, the new file size has been changed */
2497         scp->length = *sizep;
2498         scp->mask |= CM_SCACHEMASK_LENGTH;
2499     }
2500     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2501         /* really extending the file */
2502         scp->length = *sizep;
2503         scp->mask |= CM_SCACHEMASK_LENGTH;
2504     }
2505
2506     /* done successfully */
2507     code = 0;
2508
2509     cm_SyncOpDone(scp, NULL, 
2510                    CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2511                    | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2512
2513   done:
2514     lock_ReleaseWrite(&scp->rw);
2515     lock_ReleaseWrite(&scp->bufCreateLock);
2516
2517     return code;
2518 }
2519
2520 /* set the file size or other attributes (but not both at once) */
2521 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2522                 cm_req_t *reqp)
2523 {
2524     long code;
2525     AFSFetchStatus afsOutStatus;
2526     AFSVolSync volSync;
2527     cm_conn_t *connp;
2528     AFSFid tfid;
2529     AFSStoreStatus afsInStatus;
2530     struct rx_connection * rxconnp;
2531
2532     /* handle file length setting */
2533     if (attrp->mask & CM_ATTRMASK_LENGTH)
2534         return cm_SetLength(scp, &attrp->length, userp, reqp);
2535
2536     lock_ObtainWrite(&scp->rw);
2537     /* otherwise, we have to make an RPC to get the status */
2538     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2539     if (code) {
2540         lock_ReleaseWrite(&scp->rw);
2541         return code;
2542     }
2543     lock_ConvertWToR(&scp->rw);
2544
2545     /* make the attr structure */
2546     cm_StatusFromAttr(&afsInStatus, scp, attrp);
2547
2548     tfid.Volume = scp->fid.volume;
2549     tfid.Vnode = scp->fid.vnode;
2550     tfid.Unique = scp->fid.unique;
2551     lock_ReleaseRead(&scp->rw);
2552
2553     /* now make the RPC */
2554     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2555     do {
2556         code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2557         if (code) 
2558             continue;
2559
2560         rxconnp = cm_GetRxConn(connp);
2561         code = RXAFS_StoreStatus(rxconnp, &tfid,
2562                                   &afsInStatus, &afsOutStatus, &volSync);
2563         rx_PutConnection(rxconnp);
2564
2565     } while (cm_Analyze(connp, userp, reqp,
2566                          &scp->fid, &volSync, NULL, NULL, code));
2567     code = cm_MapRPCError(code, reqp);
2568
2569     if (code)
2570         osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2571     else
2572         osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2573
2574     lock_ObtainWrite(&scp->rw);
2575     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2576     if (code == 0)
2577         cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
2578                         CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2579         
2580     /* if we're changing the mode bits, discard the ACL cache, 
2581      * since we changed the mode bits.
2582      */
2583     if (afsInStatus.Mask & AFS_SETMODE) 
2584         cm_FreeAllACLEnts(scp);
2585     lock_ReleaseWrite(&scp->rw);
2586     return code;
2587 }       
2588
2589 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2590                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2591 {       
2592     cm_conn_t *connp;
2593     long code;
2594     AFSFid dirAFSFid;
2595     cm_callbackRequest_t cbReq;
2596     AFSFid newAFSFid;
2597     cm_fid_t newFid;
2598     cm_scache_t *scp = NULL;
2599     int didEnd;
2600     AFSStoreStatus inStatus;
2601     AFSFetchStatus updatedDirStatus;
2602     AFSFetchStatus newFileStatus;
2603     AFSCallBack newFileCallback;
2604     AFSVolSync volSync;
2605     struct rx_connection * rxconnp;
2606     cm_dirOp_t dirop;
2607     fschar_t * fnamep = NULL;
2608
2609     /* can't create names with @sys in them; must expand it manually first.
2610      * return "invalid request" if they try.
2611      */
2612     if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2613         return CM_ERROR_ATSYS;
2614     }
2615
2616 #ifdef AFS_FREELANCE_CLIENT
2617     /* Freelance root volume does not hold files */
2618     if (cm_freelanceEnabled &&
2619         dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2620         dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2621     {
2622         return CM_ERROR_NOACCESS;
2623     }
2624 #endif /* AFS_FREELANCE_CLIENT */
2625
2626     /* before starting the RPC, mark that we're changing the file data, so
2627      * that someone who does a chmod will know to wait until our call
2628      * completes.
2629      */
2630     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2631     lock_ObtainWrite(&dscp->rw);
2632     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2633     lock_ReleaseWrite(&dscp->rw);
2634     if (code == 0) {
2635         cm_StartCallbackGrantingCall(NULL, &cbReq);
2636     } else {
2637         cm_EndDirOp(&dirop);
2638     }
2639     if (code) {
2640         return code;
2641     }
2642     didEnd = 0;
2643
2644     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2645
2646     cm_StatusFromAttr(&inStatus, NULL, attrp);
2647
2648     /* try the RPC now */
2649     osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2650     do {
2651         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2652         if (code) 
2653             continue;
2654
2655         dirAFSFid.Volume = dscp->fid.volume;
2656         dirAFSFid.Vnode = dscp->fid.vnode;
2657         dirAFSFid.Unique = dscp->fid.unique;
2658
2659         rxconnp = cm_GetRxConn(connp);
2660         code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2661                                  &inStatus, &newAFSFid, &newFileStatus,
2662                                  &updatedDirStatus, &newFileCallback,
2663                                  &volSync);
2664         rx_PutConnection(rxconnp);
2665
2666     } while (cm_Analyze(connp, userp, reqp,
2667                          &dscp->fid, &volSync, NULL, &cbReq, code));
2668     code = cm_MapRPCError(code, reqp);
2669         
2670     if (code)
2671         osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2672     else
2673         osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2674
2675     if (dirop.scp) {
2676         lock_ObtainWrite(&dirop.scp->dirlock);
2677         dirop.lockType = CM_DIRLOCK_WRITE;
2678     }
2679     lock_ObtainWrite(&dscp->rw);
2680     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2681     if (code == 0) {
2682         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2683     }
2684     lock_ReleaseWrite(&dscp->rw);
2685
2686     /* now try to create the file's entry, too, but be careful to 
2687      * make sure that we don't merge in old info.  Since we weren't locking
2688      * out any requests during the file's creation, we may have pretty old
2689      * info.
2690      */
2691     if (code == 0) {
2692         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2693         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2694         if (code == 0) {
2695             lock_ObtainWrite(&scp->rw);
2696             scp->creator = userp;               /* remember who created it */
2697             if (!cm_HaveCallback(scp)) {
2698                 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2699                                userp, 0);
2700                 cm_EndCallbackGrantingCall(scp, &cbReq,
2701                                            &newFileCallback, 0);
2702                 didEnd = 1;     
2703             }       
2704             lock_ReleaseWrite(&scp->rw);
2705         }
2706     }
2707
2708     /* make sure we end things properly */
2709     if (!didEnd)
2710         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2711
2712     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2713         cm_DirCreateEntry(&dirop, fnamep, &newFid);
2714 #ifdef USE_BPLUS
2715         cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2716 #endif
2717     }
2718     cm_EndDirOp(&dirop);
2719
2720     if (fnamep)
2721         free(fnamep);
2722
2723     if (scp) {
2724         if (scpp)
2725             *scpp = scp;
2726         else
2727             cm_ReleaseSCache(scp);
2728     }
2729     return code;
2730 }       
2731
2732 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2733 {
2734     long code;
2735
2736     code = buf_CleanVnode(scp, userp, reqp);
2737     if (code == 0) {
2738         lock_ObtainWrite(&scp->rw);
2739
2740         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2741                           | CM_SCACHEMASK_CLIENTMODTIME
2742                           | CM_SCACHEMASK_LENGTH))
2743             code = cm_StoreMini(scp, userp, reqp);
2744
2745         if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2746             code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2747             scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2748         }
2749
2750         lock_ReleaseWrite(&scp->rw);
2751     }
2752     return code;
2753 }
2754
2755 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2756                 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2757 {
2758     cm_conn_t *connp;
2759     long code;
2760     AFSFid dirAFSFid;
2761     cm_callbackRequest_t cbReq;
2762     AFSFid newAFSFid;
2763     cm_fid_t newFid;
2764     cm_scache_t *scp = NULL;
2765     int didEnd;
2766     AFSStoreStatus inStatus;
2767     AFSFetchStatus updatedDirStatus;
2768     AFSFetchStatus newDirStatus;
2769     AFSCallBack newDirCallback;
2770     AFSVolSync volSync;
2771     struct rx_connection * rxconnp;
2772     cm_dirOp_t dirop;
2773     fschar_t * fnamep = NULL;
2774
2775     /* can't create names with @sys in them; must expand it manually first.
2776      * return "invalid request" if they try.
2777      */
2778     if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2779         return CM_ERROR_ATSYS;
2780     }
2781
2782 #ifdef AFS_FREELANCE_CLIENT
2783     /* Freelance root volume does not hold subdirectories */
2784     if (cm_freelanceEnabled &&
2785         dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2786         dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2787     {
2788         return CM_ERROR_NOACCESS;
2789     }
2790 #endif /* AFS_FREELANCE_CLIENT */
2791
2792     /* before starting the RPC, mark that we're changing the directory
2793      * data, so that someone who does a chmod on the dir will wait until
2794      * our call completes.
2795      */
2796     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2797     lock_ObtainWrite(&dscp->rw);
2798     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2799     lock_ReleaseWrite(&dscp->rw);
2800     if (code == 0) {
2801         cm_StartCallbackGrantingCall(NULL, &cbReq);
2802     } else {
2803         cm_EndDirOp(&dirop);
2804     }
2805     if (code) {
2806         return code;
2807     }
2808     didEnd = 0;
2809
2810     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2811     cm_StatusFromAttr(&inStatus, NULL, attrp);
2812
2813     /* try the RPC now */
2814     osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2815     do {
2816         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2817         if (code) 
2818             continue;
2819
2820         dirAFSFid.Volume = dscp->fid.volume;
2821         dirAFSFid.Vnode = dscp->fid.vnode;
2822         dirAFSFid.Unique = dscp->fid.unique;
2823
2824         rxconnp = cm_GetRxConn(connp);
2825         code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
2826                               &inStatus, &newAFSFid, &newDirStatus,
2827                               &updatedDirStatus, &newDirCallback,
2828                               &volSync);
2829         rx_PutConnection(rxconnp);
2830
2831     } while (cm_Analyze(connp, userp, reqp,
2832                         &dscp->fid, &volSync, NULL, &cbReq, code));
2833     code = cm_MapRPCError(code, reqp);
2834         
2835     if (code)
2836         osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2837     else
2838         osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2839
2840     if (dirop.scp) {
2841         lock_ObtainWrite(&dirop.scp->dirlock);
2842         dirop.lockType = CM_DIRLOCK_WRITE;
2843     }
2844     lock_ObtainWrite(&dscp->rw);
2845     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2846     if (code == 0) {
2847         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2848     }
2849     lock_ReleaseWrite(&dscp->rw);
2850
2851     /* now try to create the new dir's entry, too, but be careful to 
2852      * make sure that we don't merge in old info.  Since we weren't locking
2853      * out any requests during the file's creation, we may have pretty old
2854      * info.
2855      */
2856     if (code == 0) {
2857         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2858         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2859         if (code == 0) {
2860             lock_ObtainWrite(&scp->rw);
2861             if (!cm_HaveCallback(scp)) {
2862                 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2863                                 userp, 0);
2864                 cm_EndCallbackGrantingCall(scp, &cbReq,
2865                                             &newDirCallback, 0);
2866                 didEnd = 1;             
2867             }
2868             lock_ReleaseWrite(&scp->rw);
2869         }
2870     }
2871
2872     /* make sure we end things properly */
2873     if (!didEnd)
2874         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2875
2876     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2877         cm_DirCreateEntry(&dirop, fnamep, &newFid);
2878 #ifdef USE_BPLUS
2879         cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2880 #endif
2881     }
2882     cm_EndDirOp(&dirop);
2883
2884     free(fnamep);
2885
2886     if (scp) {
2887         if (scpp)
2888             *scpp = scp;
2889         else
2890             cm_ReleaseSCache(scp);
2891     }
2892
2893     /* and return error code */
2894     return code;
2895 }       
2896
2897 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
2898              cm_user_t *userp, cm_req_t *reqp)
2899 {
2900     cm_conn_t *connp;
2901     long code = 0;
2902     AFSFid dirAFSFid;
2903     AFSFid existingAFSFid;
2904     AFSFetchStatus updatedDirStatus;
2905     AFSFetchStatus newLinkStatus;
2906     AFSVolSync volSync;
2907     struct rx_connection * rxconnp;
2908     cm_dirOp_t dirop;
2909     fschar_t * fnamep = NULL;
2910
2911     if (dscp->fid.cell != sscp->fid.cell ||
2912         dscp->fid.volume != sscp->fid.volume) {
2913         return CM_ERROR_CROSSDEVLINK;
2914     }
2915
2916     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2917     lock_ObtainWrite(&dscp->rw);
2918     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2919     lock_ReleaseWrite(&dscp->rw);
2920     if (code != 0)
2921         cm_EndDirOp(&dirop);
2922
2923     if (code)
2924         return code;
2925
2926     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2927
2928     /* try the RPC now */
2929     osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2930     do {
2931         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2932         if (code) continue;
2933
2934         dirAFSFid.Volume = dscp->fid.volume;
2935         dirAFSFid.Vnode = dscp->fid.vnode;
2936         dirAFSFid.Unique = dscp->fid.unique;
2937
2938         existingAFSFid.Volume = sscp->fid.volume;
2939         existingAFSFid.Vnode = sscp->fid.vnode;
2940         existingAFSFid.Unique = sscp->fid.unique;
2941
2942         rxconnp = cm_GetRxConn(connp);
2943         code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
2944             &newLinkStatus, &updatedDirStatus, &volSync);
2945         rx_PutConnection(rxconnp);
2946         osi_Log1(afsd_logp,"  RXAFS_Link returns 0x%x", code);
2947
2948     } while (cm_Analyze(connp, userp, reqp,
2949         &dscp->fid, &volSync, NULL, NULL, code));
2950
2951     code = cm_MapRPCError(code, reqp);
2952
2953     if (code)
2954         osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2955     else
2956         osi_Log0(afsd_logp, "CALL Link SUCCESS");
2957
2958     if (dirop.scp) {
2959         lock_ObtainWrite(&dirop.scp->dirlock);
2960         dirop.lockType = CM_DIRLOCK_WRITE;
2961     }
2962     lock_ObtainWrite(&dscp->rw);
2963     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2964     if (code == 0) {
2965         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2966     }
2967     lock_ReleaseWrite(&dscp->rw);
2968
2969     if (code == 0) {
2970         if (cm_CheckDirOpForSingleChange(&dirop)) {
2971             cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
2972 #ifdef USE_BPLUS
2973             cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
2974 #endif
2975         }
2976     }
2977     cm_EndDirOp(&dirop);
2978
2979     free(fnamep);
2980
2981     return code;
2982 }
2983
2984 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
2985                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2986 {
2987     cm_conn_t *connp;
2988     long code;
2989     AFSFid dirAFSFid;
2990     AFSFid newAFSFid;
2991     cm_fid_t newFid;
2992     cm_scache_t *scp;
2993     AFSStoreStatus inStatus;
2994     AFSFetchStatus updatedDirStatus;
2995     AFSFetchStatus newLinkStatus;
2996     AFSVolSync volSync;
2997     struct rx_connection * rxconnp;
2998     cm_dirOp_t dirop;
2999     fschar_t *fnamep = NULL;
3000
3001     /* before starting the RPC, mark that we're changing the directory data,
3002      * so that someone who does a chmod on the dir will wait until our
3003      * call completes.
3004      */
3005     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3006     lock_ObtainWrite(&dscp->rw);
3007     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3008     lock_ReleaseWrite(&dscp->rw);
3009     if (code != 0)
3010         cm_EndDirOp(&dirop);
3011     if (code) {
3012         return code;
3013     }
3014
3015     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3016
3017     cm_StatusFromAttr(&inStatus, NULL, attrp);
3018
3019     /* try the RPC now */
3020     osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3021     do {
3022         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3023         if (code) 
3024             continue;
3025
3026         dirAFSFid.Volume = dscp->fid.volume;
3027         dirAFSFid.Vnode = dscp->fid.vnode;
3028         dirAFSFid.Unique = dscp->fid.unique;
3029
3030         rxconnp = cm_GetRxConn(connp);
3031         code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3032                               &inStatus, &newAFSFid, &newLinkStatus,
3033                               &updatedDirStatus, &volSync);
3034         rx_PutConnection(rxconnp);
3035
3036     } while (cm_Analyze(connp, userp, reqp,
3037                          &dscp->fid, &volSync, NULL, NULL, code));
3038     code = cm_MapRPCError(code, reqp);
3039         
3040     if (code)
3041         osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3042     else
3043         osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3044
3045     if (dirop.scp) {
3046         lock_ObtainWrite(&dirop.scp->dirlock);
3047         dirop.lockType = CM_DIRLOCK_WRITE;
3048     }
3049     lock_ObtainWrite(&dscp->rw);
3050     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3051     if (code == 0) {
3052         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3053     }
3054     lock_ReleaseWrite(&dscp->rw);
3055
3056     if (code == 0) {
3057         if (cm_CheckDirOpForSingleChange(&dirop)) {
3058             cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3059
3060             cm_DirCreateEntry(&dirop, fnamep, &newFid);
3061 #ifdef USE_BPLUS
3062             cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3063 #endif
3064         }
3065     }
3066     cm_EndDirOp(&dirop);
3067
3068     /* now try to create the new dir's entry, too, but be careful to 
3069      * make sure that we don't merge in old info.  Since we weren't locking
3070      * out any requests during the file's creation, we may have pretty old
3071      * info.
3072      */
3073     if (code == 0) {
3074         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3075         code = cm_GetSCache(&newFid, &scp, userp, reqp);
3076         if (code == 0) {
3077             lock_ObtainWrite(&scp->rw);
3078             if (!cm_HaveCallback(scp)) {
3079                 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3080                                 userp, 0);
3081             }       
3082             lock_ReleaseWrite(&scp->rw);
3083             cm_ReleaseSCache(scp);
3084         }
3085     }
3086
3087     free(fnamep);
3088         
3089     /* and return error code */
3090     return code;
3091 }
3092
3093 /*! \brief Remove a directory
3094
3095   Encapsulates a call to RXAFS_RemoveDir().
3096
3097   \param[in] dscp cm_scache_t for the directory containing the
3098       directory to be removed.
3099
3100   \param[in] fnamep This will be the original name of the directory
3101       as known to the file server.   It will be passed in to RXAFS_RemoveDir().
3102       This parameter is optional.  If it is not provided the value
3103       will be looked up.
3104
3105   \param[in] cnamep Normalized name used to update the local
3106       directory caches.
3107
3108   \param[in] userp cm_user_t for the request.
3109
3110   \param[in] reqp Request tracker.
3111 */
3112 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3113 {
3114     cm_conn_t *connp;
3115     long code;
3116     AFSFid dirAFSFid;
3117     int didEnd;
3118     AFSFetchStatus updatedDirStatus;
3119     AFSVolSync volSync;
3120     struct rx_connection * rxconnp;
3121     cm_dirOp_t dirop;
3122     cm_scache_t *scp = NULL;
3123     int free_fnamep = FALSE;
3124
3125     if (fnamep == NULL) {
3126         code = -1;
3127 #ifdef USE_BPLUS
3128         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
3129         if (code == 0) {
3130             code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3131             if (code == 0)
3132                 free_fnamep = TRUE;
3133             cm_EndDirOp(&dirop);
3134         }
3135 #endif
3136         if (code)
3137             goto done;
3138     }
3139
3140     code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3141     if (code)
3142         goto done;
3143
3144     /* before starting the RPC, mark that we're changing the directory data,
3145      * so that someone who does a chmod on the dir will wait until our
3146      * call completes.
3147      */
3148     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3149     lock_ObtainWrite(&dscp->rw);
3150     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3151     lock_ReleaseWrite(&dscp->rw);
3152     if (code) {
3153         cm_EndDirOp(&dirop);
3154         goto done;
3155     }
3156     didEnd = 0;
3157
3158     /* try the RPC now */
3159     osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3160     do {
3161         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3162         if (code) 
3163             continue;
3164
3165         dirAFSFid.Volume = dscp->fid.volume;
3166         dirAFSFid.Vnode = dscp->fid.vnode;
3167         dirAFSFid.Unique = dscp->fid.unique;
3168
3169         rxconnp = cm_GetRxConn(connp);
3170         code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3171                                &updatedDirStatus, &volSync);
3172         rx_PutConnection(rxconnp);
3173
3174     } while (cm_Analyze(connp, userp, reqp,
3175                         &dscp->fid, &volSync, NULL, NULL, code));
3176     code = cm_MapRPCErrorRmdir(code, reqp);
3177
3178     if (code)
3179         osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3180     else
3181         osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3182
3183     if (dirop.scp) {
3184         lock_ObtainWrite(&dirop.scp->dirlock);
3185         dirop.lockType = CM_DIRLOCK_WRITE;
3186     }
3187     lock_ObtainWrite(&dscp->rw);
3188     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3189     if (code == 0) {
3190         cm_dnlcRemove(dscp, cnamep); 
3191         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3192     }
3193     lock_ReleaseWrite(&dscp->rw);
3194
3195     if (code == 0) {
3196         if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3197             cm_DirDeleteEntry(&dirop, fnamep);
3198 #ifdef USE_BPLUS
3199             cm_BPlusDirDeleteEntry(&dirop, cnamep);
3200 #endif
3201         }
3202     }
3203     cm_EndDirOp(&dirop);
3204
3205     if (scp) {
3206         cm_ReleaseSCache(scp);
3207         if (code == 0) {
3208             lock_ObtainWrite(&scp->rw);
3209             scp->flags |= CM_SCACHEFLAG_DELETED;
3210             lock_ReleaseWrite(&scp->rw);
3211         }
3212     }
3213
3214   done:
3215     if (free_fnamep)
3216         free(fnamep);
3217
3218     /* and return error code */
3219     return code;
3220 }
3221
3222 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3223 {
3224     /* grab mutex on contents */
3225     lock_ObtainWrite(&scp->rw);
3226
3227     /* reset the prefetch info */
3228     scp->prefetch.base.LowPart = 0;             /* base */
3229     scp->prefetch.base.HighPart = 0;
3230     scp->prefetch.end.LowPart = 0;              /* and end */
3231     scp->prefetch.end.HighPart = 0;
3232
3233     /* release mutex on contents */
3234     lock_ReleaseWrite(&scp->rw);
3235
3236     /* we're done */
3237     return 0;
3238 }       
3239
3240 /*! \brief Rename a file or directory
3241
3242   Encapsulates a RXAFS_Rename() call.
3243
3244   \param[in] oldDscp cm_scache_t for the directory containing the old
3245       name.
3246
3247   \param[in] oldNamep The original old name known to the file server.
3248       This is the name that will be passed into the RXAFS_Rename().
3249       If it is not provided, it will be looked up.
3250
3251   \param[in] normalizedOldNamep Normalized old name.  This is used for
3252   updating local directory caches.
3253
3254   \param[in] newDscp cm_scache_t for the directory containing the new
3255   name.
3256
3257   \param[in] newNamep New name. Normalized.
3258
3259   \param[in] userp cm_user_t for the request.
3260
3261   \param[in,out] reqp Request tracker.
3262
3263 */
3264 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3265                cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3266                cm_req_t *reqp)
3267 {
3268     cm_conn_t *connp;
3269     long code;
3270     AFSFid oldDirAFSFid;
3271     AFSFid newDirAFSFid;
3272     int didEnd;
3273     AFSFetchStatus updatedOldDirStatus;
3274     AFSFetchStatus updatedNewDirStatus;
3275     AFSVolSync volSync;
3276     int oneDir;
3277     struct rx_connection * rxconnp;
3278     cm_dirOp_t oldDirOp;
3279     cm_fid_t   fileFid;
3280     int        diropCode = -1;
3281     cm_dirOp_t newDirOp;
3282     fschar_t * newNamep = NULL;
3283     int free_oldNamep = FALSE;
3284
3285     if (cOldNamep == NULL || cNewNamep == NULL ||
3286         cm_ClientStrLen(cOldNamep) == 0 ||
3287         cm_ClientStrLen(cNewNamep) == 0)
3288         return CM_ERROR_INVAL;
3289
3290     if (oldNamep == NULL) {
3291         code = -1;
3292 #ifdef USE_BPLUS
3293         code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ, &oldDirOp);
3294         if (code == 0) {
3295             code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3296             if (code == 0)
3297                 free_oldNamep = TRUE;
3298             cm_EndDirOp(&oldDirOp);
3299         }
3300 #endif
3301         if (code) {
3302             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed", 
3303                       oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3304             goto done;
3305         }
3306     }
3307
3308
3309     /* before starting the RPC, mark that we're changing the directory data,
3310      * so that someone who does a chmod on the dir will wait until our call
3311      * completes.  We do this in vnode order so that we don't deadlock,
3312      * which makes the code a little verbose.
3313      */
3314     if (oldDscp == newDscp) {
3315         /* check for identical names */
3316         if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3317             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL", 
3318                       oldDscp, newDscp);
3319             code = CM_ERROR_RENAME_IDENTICAL;
3320             goto done;
3321         }
3322
3323         oneDir = 1;
3324         cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3325         lock_ObtainWrite(&oldDscp->rw);
3326         cm_dnlcRemove(oldDscp, cOldNamep);
3327         cm_dnlcRemove(oldDscp, cNewNamep);
3328         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3329                           CM_SCACHESYNC_STOREDATA);
3330         lock_ReleaseWrite(&oldDscp->rw);
3331         if (code != 0) {
3332             cm_EndDirOp(&oldDirOp);
3333         }
3334     }
3335     else {
3336         /* two distinct dir vnodes */
3337         oneDir = 0;
3338         if (oldDscp->fid.cell != newDscp->fid.cell ||
3339              oldDscp->fid.volume != newDscp->fid.volume) {
3340             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK", 
3341                       oldDscp, newDscp);
3342             code = CM_ERROR_CROSSDEVLINK;
3343             goto done;
3344         }
3345
3346         /* shouldn't happen that we have distinct vnodes for two
3347          * different files, but could due to deliberate attack, or
3348          * stale info.  Avoid deadlocks and quit now.
3349          */
3350         if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3351             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision", 
3352                       oldDscp, newDscp);
3353             code = CM_ERROR_CROSSDEVLINK;
3354             goto done;
3355         }
3356
3357         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3358             cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3359             lock_ObtainWrite(&oldDscp->rw);
3360             cm_dnlcRemove(oldDscp, cOldNamep);
3361             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3362                              CM_SCACHESYNC_STOREDATA);
3363             lock_ReleaseWrite(&oldDscp->rw);
3364             if (code != 0)
3365                 cm_EndDirOp(&oldDirOp);
3366             if (code == 0) {
3367                 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3368                 lock_ObtainWrite(&newDscp->rw);
3369                 cm_dnlcRemove(newDscp, cNewNamep);
3370                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3371                                  CM_SCACHESYNC_STOREDATA);
3372                 lock_ReleaseWrite(&newDscp->rw);
3373                 if (code) {
3374                     cm_EndDirOp(&newDirOp);
3375
3376                     /* cleanup first one */
3377                     lock_ObtainWrite(&oldDscp->rw);
3378                     cm_SyncOpDone(oldDscp, NULL,
3379                                    CM_SCACHESYNC_STOREDATA);
3380                     lock_ReleaseWrite(&oldDscp->rw);
3381                     cm_EndDirOp(&oldDirOp);
3382                 }       
3383             }
3384         }
3385         else {
3386             /* lock the new vnode entry first */
3387             cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3388             lock_ObtainWrite(&newDscp->rw);
3389             cm_dnlcRemove(newDscp, cNewNamep);
3390             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3391                               CM_SCACHESYNC_STOREDATA);
3392             lock_ReleaseWrite(&newDscp->rw);
3393             if (code != 0)
3394                 cm_EndDirOp(&newDirOp);
3395             if (code == 0) {
3396                 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3397                 lock_ObtainWrite(&oldDscp->rw);
3398                 cm_dnlcRemove(oldDscp, cOldNamep);
3399                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3400                                   CM_SCACHESYNC_STOREDATA);
3401                 lock_ReleaseWrite(&oldDscp->rw);
3402                 if (code != 0)
3403                     cm_EndDirOp(&oldDirOp);
3404                 if (code) {
3405                     /* cleanup first one */
3406                     lock_ObtainWrite(&newDscp->rw);
3407                     cm_SyncOpDone(newDscp, NULL,
3408                                    CM_SCACHESYNC_STOREDATA);
3409                     lock_ReleaseWrite(&newDscp->rw);
3410                     cm_EndDirOp(&newDirOp);
3411                 }       
3412             }
3413         }
3414     }   /* two distinct vnodes */
3415
3416     if (code) 
3417         goto done;
3418
3419     didEnd = 0;
3420
3421     newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3422
3423     /* try the RPC now */
3424     osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p", 
3425               oldDscp, newDscp);
3426     do {
3427         code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3428         if (code) 
3429             continue;
3430
3431         oldDirAFSFid.Volume = oldDscp->fid.volume;
3432         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3433         oldDirAFSFid.Unique = oldDscp->fid.unique;
3434         newDirAFSFid.Volume = newDscp->fid.volume;
3435         newDirAFSFid.Vnode = newDscp->fid.vnode;
3436         newDirAFSFid.Unique = newDscp->fid.unique;
3437
3438         rxconnp = cm_GetRxConn(connp);
3439         code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3440                             &newDirAFSFid, newNamep,
3441                             &updatedOldDirStatus, &updatedNewDirStatus,
3442                             &volSync);
3443         rx_PutConnection(rxconnp);
3444
3445     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3446                          &volSync, NULL, NULL, code));
3447     code = cm_MapRPCError(code, reqp);
3448         
3449     if (code)
3450         osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3451     else
3452         osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3453
3454     /* update the individual stat cache entries for the directories */
3455     if (oldDirOp.scp) {
3456         lock_ObtainWrite(&oldDirOp.scp->dirlock);
3457         oldDirOp.lockType = CM_DIRLOCK_WRITE;
3458     }
3459     lock_ObtainWrite(&oldDscp->rw);
3460     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3461
3462     if (code == 0)
3463         cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3464                        userp, CM_MERGEFLAG_DIROP);
3465     lock_ReleaseWrite(&oldDscp->rw);
3466
3467     if (code == 0) {
3468         if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
3469
3470 #ifdef USE_BPLUS
3471             diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3472             if (diropCode == CM_ERROR_INEXACT_MATCH)
3473                 diropCode = 0;
3474             else if (diropCode == EINVAL)
3475 #endif
3476                 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3477
3478             if (diropCode == 0) {
3479                 if (oneDir) {
3480                     diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3481 #ifdef USE_BPLUS
3482                     cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3483 #endif
3484                 }
3485                 
3486                 if (diropCode == 0) { 
3487                     diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3488 #ifdef USE_BPLUS
3489                     cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3490 #endif
3491                 }
3492             }
3493         }
3494     }
3495     cm_EndDirOp(&oldDirOp);
3496
3497     /* and update it for the new one, too, if necessary */
3498     if (!oneDir) {
3499         if (newDirOp.scp) {
3500             lock_ObtainWrite(&newDirOp.scp->dirlock);
3501             newDirOp.lockType = CM_DIRLOCK_WRITE;
3502         }
3503         lock_ObtainWrite(&newDscp->rw);
3504         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3505         if (code == 0)
3506             cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3507                             userp, CM_MERGEFLAG_DIROP);
3508         lock_ReleaseWrite(&newDscp->rw);
3509
3510         if (code == 0) {
3511             /* we only make the local change if we successfully made
3512                the change in the old directory AND there was only one
3513                change in the new directory */
3514             if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3515                 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3516 #ifdef USE_BPLUS
3517                 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3518 #endif
3519             }
3520         }
3521         cm_EndDirOp(&newDirOp);
3522     }
3523
3524   done:
3525     if (free_oldNamep)
3526         free(oldNamep);
3527
3528     free(newNamep);
3529
3530     /* and return error code */
3531     return code;
3532 }
3533
3534 /* Byte range locks:
3535
3536    The OpenAFS Windows client has to fake byte range locks given no
3537    server side support for such locks.  This is implemented as keyed
3538    byte range locks on the cache manager.
3539
3540    Keyed byte range locks:
3541
3542    Each cm_scache_t structure keeps track of a list of keyed locks.
3543    The key for a lock identifies an owner of a set of locks (referred
3544    to as a client).  Each key is represented by a value.  The set of
3545    key values used within a specific cm_scache_t structure form a
3546    namespace that has a scope of just that cm_scache_t structure.  The
3547    same key value can be used with another cm_scache_t structure and
3548    correspond to a completely different client.  However it is
3549    advantageous for the SMB or IFS layer to make sure that there is a
3550    1-1 mapping between client and keys over all cm_scache_t objects.
3551
3552    Assume a client C has key Key(C) (although, since the scope of the
3553    key is a cm_scache_t, the key can be Key(C,S), where S is the
3554    cm_scache_t.  But assume a 1-1 relation between keys and clients).
3555    A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3556    inclusive (a.k.a. [O,O+L-1]).  The function Key(x) is implemented
3557    through cm_generateKey() function for both SMB and IFS.
3558
3559    The list of locks for a cm_scache_t object S is maintained in
3560    S->fileLocks.  The cache manager will set a lock on the AFS file
3561    server in order to assert the locks in S->fileLocks.  If only
3562    shared locks are in place for S, then the cache manager will obtain
3563    a LockRead lock, while if there are any exclusive locks, it will
3564    obtain a LockWrite lock.  If the exclusive locks are all released
3565    while the shared locks remain, then the cache manager will
3566    downgrade the lock from LockWrite to LockRead.  Similarly, if an
3567    exclusive lock is obtained when only shared locks exist, then the
3568    cache manager will try to upgrade the lock from LockRead to
3569    LockWrite.
3570
3571    Each lock L owned by client C maintains a key L->key such that
3572    L->key == Key(C), the effective range defined by L->LOffset and
3573    L->LLength such that the range of bytes affected by the lock is
3574    (L->LOffset, +L->LLength), a type maintained in L->LockType which
3575    is either exclusive or shared.
3576
3577    Lock states:
3578
3579    A lock exists iff it is in S->fileLocks for some cm_scache_t
3580    S. Existing locks are in one of the following states: ACTIVE,
3581    WAITLOCK, WAITUNLOCK, LOST, DELETED.
3582
3583    The following sections describe each lock and the associated
3584    transitions.
3585
3586    1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3587       the lock with the AFS file server.  This type of lock can be
3588       exercised by a client to read or write to the locked region (as
3589       the lock allows).
3590
3591       1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3592         server lock that was required to assert the lock.  Before
3593         marking the lock as lost, the cache manager checks if the file
3594         has changed on the server.  If the file has not changed, then
3595         the cache manager will attempt to obtain a new server lock
3596         that is sufficient to assert the client side locks for the
3597         file.  If any of these fail, the lock is marked as LOST.
3598         Otherwise, it is left as ACTIVE.
3599
3600       1.2 ACTIVE->DELETED: Lock is released.
3601
3602    2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3603       grants the lock but the lock is yet to be asserted with the AFS
3604       file server.  Once the file server grants the lock, the state
3605       will transition to an ACTIVE lock.
3606
3607       2.1 WAITLOCK->ACTIVE: The server granted the lock.
3608
3609       2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3610         waiting.
3611
3612       2.3 WAITLOCK->LOST: One or more locks from this client were
3613         marked as LOST.  No further locks will be granted to this
3614         client until all lost locks are removed.
3615
3616    3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3617       receives a request for a lock that conflicts with an existing
3618       ACTIVE or WAITLOCK lock.  The lock will be placed in the queue
3619       and will be granted at such time the conflicting locks are
3620       removed, at which point the state will transition to either
3621       WAITLOCK or ACTIVE.
3622
3623       3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed.  The
3624         current serverLock is sufficient to assert this lock, or a
3625         sufficient serverLock is obtained.
3626
3627       3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3628         however the required serverLock is yet to be asserted with the
3629         server.
3630
3631       3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3632         released.
3633
3634       3.5 WAITUNLOCK->LOST: One or more locks from this client were
3635         marked as LOST.  No further locks will be granted to this
3636         client until all lost locks are removed.
3637
3638    4. LOST: A lock L is LOST if the server lock that was required to
3639       assert the lock could not be obtained or if it could not be
3640       extended, or if other locks by the same client were LOST.
3641       Essentially, once a lock is LOST, the contract between the cache
3642       manager and that specific client is no longer valid.
3643
3644       The cache manager rechecks the server lock once every minute and
3645       extends it as appropriate.  If this is not done for 5 minutes,
3646       the AFS file server will release the lock (the 5 minute timeout
3647       is based on current file server code and is fairly arbitrary).
3648       Once released, the lock cannot be re-obtained without verifying
3649       that the contents of the file hasn't been modified since the
3650       time the lock was released.  Re-obtaining the lock without
3651       verifying this may lead to data corruption.  If the lock can not
3652       be obtained safely, then all active locks for the cm_scache_t
3653       are marked as LOST.
3654
3655       4.1 LOST->DELETED: The lock is released.
3656
3657    5. DELETED: The lock is no longer relevant.  Eventually, it will
3658       get removed from the cm_scache_t. In the meantime, it will be
3659       treated as if it does not exist.
3660
3661       5.1 DELETED->not exist: The lock is removed from the
3662         cm_scache_t.
3663
3664    The following are classifications of locks based on their state.
3665
3666    6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK.  These locks
3667       have been accepted by the cache manager, but may or may not have
3668       been granted back to the client.
3669
3670    7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3671
3672    8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3673
3674    Lock operation:
3675
3676    A client C can READ range (Offset,+Length) of a file represented by
3677    cm_scache_t S iff (1):
3678
3679    1. for all _a_ in (Offset,+Length), all of the following is true:
3680
3681        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3682          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3683          shared.
3684
3685        1.2 For each LOST lock L in S->fileLocks such that _a_ in
3686          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3687          Key(C)
3688
3689        (When locks are lost on an cm_scache_t, all locks are lost.  By
3690        4.2 (below), if there is an exclusive LOST lock, then there
3691        can't be any overlapping ACTIVE locks.)
3692
3693    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3694
3695    2. for all _a_ in (Offset,+Length), one of the following is true:
3696
3697        2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3698          does not exist a LOST lock L such that _a_ in
3699          (L->LOffset,+L->LLength).
3700
3701        2.2 Byte _a_ of S is owned by C under lock L (as specified in
3702          1.2) AND L->LockType is exclusive.
3703
3704    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3705
3706    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3707       true:
3708
3709        3.1 If L->LockType is exclusive then there does NOT exist a
3710          ACCEPTED lock M in S->fileLocks such that _a_ in
3711          (M->LOffset,+M->LLength).
3712
3713          (If we count all QUEUED locks then we hit cases such as
3714          cascading waiting locks where the locks later on in the queue
3715          can be granted without compromising file integrity.  On the
3716          other hand if only ACCEPTED locks are considered, then locks
3717          that were received earlier may end up waiting for locks that
3718          were received later to be unlocked. The choice of ACCEPTED
3719          locks was made to mimic the Windows byte range lock
3720          semantics.)
3721
3722        3.2 If L->LockType is shared then for each ACCEPTED lock M in
3723          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3724          M->LockType is shared.
3725
3726    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3727
3728        4.1 M->key != Key(C)
3729
3730        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3731          and (M->LOffset,+M->LLength) do not intersect.
3732
3733          (Note: If a client loses a lock, it loses all locks.
3734          Subsequently, it will not be allowed to obtain any more locks
3735          until all existing LOST locks that belong to the client are
3736          released.  Once all locks are released by a single client,
3737          there exists no further contract between the client and AFS
3738          about the contents of the file, hence the client can then
3739          proceed to obtain new locks and establish a new contract.
3740
3741          This doesn't quite work as you think it should, because most
3742          applications aren't built to deal with losing locks they
3743          thought they once had.  For now, we don't have a good
3744          solution to lost locks.
3745
3746          Also, for consistency reasons, we have to hold off on
3747          granting locks that overlap exclusive LOST locks.)
3748
3749    A client C can only unlock locks L in S->fileLocks which have
3750    L->key == Key(C).
3751
3752    The representation and invariants are as follows:
3753
3754    - Each cm_scache_t structure keeps:
3755
3756        - A queue of byte-range locks (cm_scache_t::fileLocks) which
3757          are of type cm_file_lock_t.
3758
3759        - A record of the highest server-side lock that has been
3760          obtained for this object (cm_scache_t::serverLock), which is
3761          one of (-1), LockRead, LockWrite.
3762
3763        - A count of ACCEPTED exclusive and shared locks that are in the
3764          queue (cm_scache_t::sharedLocks and
3765          cm_scache_t::exclusiveLocks)
3766
3767    - Each cm_file_lock_t structure keeps:
3768
3769        - The type of lock (cm_file_lock_t::LockType)
3770
3771        - The key associated with the lock (cm_file_lock_t::key)
3772
3773        - The offset and length of the lock (cm_file_lock_t::LOffset
3774          and cm_file_lock_t::LLength)
3775
3776        - The state of the lock.
3777
3778        - Time of issuance or last successful extension
3779
3780    Semantic invariants:
3781
3782        I1. The number of ACCEPTED locks in S->fileLocks are
3783            (S->sharedLocks + S->exclusiveLocks)
3784
3785    External invariants:
3786
3787        I3. S->serverLock is the lock that we have asserted with the
3788            AFS file server for this cm_scache_t.
3789
3790        I4. S->serverLock == LockRead iff there is at least one ACTIVE
3791            shared lock, but no ACTIVE exclusive locks.
3792
3793        I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3794            exclusive lock.
3795
3796        I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3797            M->key == L->key IMPLIES M is LOST or DELETED.
3798
3799    --asanka
3800  */
3801
3802 #define IS_LOCK_ACTIVE(lockp)     (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3803
3804 #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)
3805
3806 #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)
3807
3808 #define IS_LOCK_LOST(lockp)       (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3809
3810 #define IS_LOCK_DELETED(lockp)    (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3811
3812 /* unsafe */
3813 #define IS_LOCK_ACCEPTED(lockp)   (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3814
3815 /* unsafe */
3816 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3817
3818 /* unsafe */
3819 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3820
3821 /* unsafe */
3822 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3823
3824 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3825 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3826 #else
3827 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3828 #endif
3829
3830 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3831
3832 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3833 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3834 #else
3835 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3836
3837 /* This should really be defined in any build that this code is being
3838    compiled. */
3839 #error  VICED_CAPABILITY_WRITELOCKACL not defined.
3840 #endif
3841
3842 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3843 {
3844     afs_int64 int_begin;
3845     afs_int64 int_end;
3846
3847     int_begin = MAX(pos->offset, neg->offset);
3848     int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3849
3850     if (int_begin < int_end) {
3851         if (int_begin == pos->offset) {
3852             pos->length = pos->offset + pos->length - int_end;
3853             pos->offset = int_end;
3854         } else if (int_end == pos->offset + pos->length) {
3855             pos->length = int_begin - pos->offset;
3856         }
3857
3858         /* We only subtract ranges if the resulting range is
3859            contiguous.  If we try to support non-contigous ranges, we
3860            aren't actually improving performance. */
3861     }
3862 }
3863
3864 /* Called with scp->rw held.  Returns 0 if all is clear to read the
3865    specified range by the client identified by key.
3866  */
3867 long cm_LockCheckRead(cm_scache_t *scp, 
3868                       LARGE_INTEGER LOffset, 
3869                       LARGE_INTEGER LLength, 
3870                       cm_key_t key)
3871 {
3872 #ifndef ADVISORY_LOCKS
3873
3874     cm_file_lock_t *fileLock;
3875     osi_queue_t *q;
3876     long code = 0;
3877     cm_range_t range;
3878     int substract_ranges = FALSE;
3879
3880     range.offset = LOffset.QuadPart;
3881     range.length = LLength.QuadPart;
3882
3883     /*
3884
3885      1. for all _a_ in (Offset,+Length), all of the following is true:
3886
3887        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3888          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3889          shared.
3890
3891        1.2 For each LOST lock L in S->fileLocks such that _a_ in
3892          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3893          Key(C)
3894
3895     */
3896
3897     lock_ObtainRead(&cm_scacheLock);
3898
3899     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3900         fileLock = 
3901             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3902
3903         if (INTERSECT_RANGE(range, fileLock->range)) {
3904             if (IS_LOCK_ACTIVE(fileLock)) {
3905                 if (fileLock->key == key) {
3906
3907                     /* If there is an active lock for this client, it
3908                        is safe to substract ranges.*/
3909                     cm_LockRangeSubtract(&range, &fileLock->range);
3910                     substract_ranges = TRUE;
3911                 } else {
3912                     if (fileLock->lockType != LockRead) {
3913                         code = CM_ERROR_LOCK_CONFLICT;
3914                         break;
3915                     }
3916
3917                     /* even if the entire range is locked for reading,
3918                        we still can't grant the lock at this point
3919                        because the client may have lost locks. That
3920                        is, unless we have already seen an active lock
3921                        belonging to the client, in which case there
3922                        can't be any lost locks for this client. */
3923                     if (substract_ranges)
3924                         cm_LockRangeSubtract(&range, &fileLock->range);
3925                 }
3926             } else if (IS_LOCK_LOST(fileLock) &&
3927                       (fileLock->key == key || fileLock->lockType == LockWrite)) {
3928                 code = CM_ERROR_BADFD;
3929                 break;
3930             }
3931         }
3932     }
3933
3934     lock_ReleaseRead(&cm_scacheLock);
3935
3936     osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3937               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3938
3939     return code;
3940
3941 #else
3942
3943     return 0;
3944
3945 #endif
3946 }
3947
3948 /* Called with scp->rw held.  Returns 0 if all is clear to write the
3949    specified range by the client identified by key.
3950  */
3951 long cm_LockCheckWrite(cm_scache_t *scp,
3952                        LARGE_INTEGER LOffset,
3953                        LARGE_INTEGER LLength,
3954                        cm_key_t key)
3955 {
3956 #ifndef ADVISORY_LOCKS
3957
3958     cm_file_lock_t *fileLock;
3959     osi_queue_t *q;
3960     long code = 0;
3961     cm_range_t range;
3962
3963     range.offset = LOffset.QuadPart;
3964     range.length = LLength.QuadPart;
3965
3966     /*
3967    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3968
3969    2. for all _a_ in (Offset,+Length), one of the following is true:
3970
3971        2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3972          lock L such that _a_ in (L->LOffset,+L->LLength).
3973
3974        2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3975          exclusive.
3976     */
3977
3978     lock_ObtainRead(&cm_scacheLock);
3979
3980     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3981         fileLock = 
3982             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3983
3984         if (INTERSECT_RANGE(range, fileLock->range)) {
3985             if (IS_LOCK_ACTIVE(fileLock)) {
3986                 if (fileLock->key == key) {
3987                     if (fileLock->lockType == LockWrite) {
3988
3989                         /* if there is an active lock for this client, it
3990                            is safe to substract ranges */
3991                         cm_LockRangeSubtract(&range, &fileLock->range);
3992                     } else {
3993                         code = CM_ERROR_LOCK_CONFLICT;
3994                         break;
3995                     }
3996                 } else {
3997                     code = CM_ERROR_LOCK_CONFLICT;
3998                     break;
3999                 }
4000             } else if (IS_LOCK_LOST(fileLock)) {
4001                 code = CM_ERROR_BADFD;
4002                 break;
4003             }
4004         }
4005     }
4006
4007     lock_ReleaseRead(&cm_scacheLock);
4008
4009     osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4010               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4011
4012     return code;
4013
4014 #else
4015
4016     return 0;
4017
4018 #endif
4019 }
4020
4021 /* Called with cm_scacheLock write locked */
4022 static cm_file_lock_t * cm_GetFileLock(void) {
4023     cm_file_lock_t * l;
4024
4025     l = (cm_file_lock_t *) cm_freeFileLocks;
4026     if (l) {
4027         osi_QRemove(&cm_freeFileLocks, &l->q);
4028     } else {
4029         l = malloc(sizeof(cm_file_lock_t));
4030         osi_assertx(l, "null cm_file_lock_t");
4031     }
4032
4033     memset(l, 0, sizeof(cm_file_lock_t));
4034
4035     return l;
4036 }
4037
4038 /* Called with cm_scacheLock write locked */
4039 static void cm_PutFileLock(cm_file_lock_t *l) {
4040     osi_QAdd(&cm_freeFileLocks, &l->q);
4041 }
4042
4043 /* called with scp->rw held.  May release it during processing, but
4044    leaves it held on exit. */
4045 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4046                    cm_req_t * reqp) {
4047     long code = 0;
4048     AFSFid tfid;
4049     cm_fid_t cfid;
4050     cm_conn_t * connp;
4051     struct rx_connection * rxconnp;
4052     AFSVolSync volSync;
4053     afs_uint32 reqflags = reqp->flags;
4054
4055     tfid.Volume = scp->fid.volume;
4056     tfid.Vnode = scp->fid.vnode;
4057     tfid.Unique = scp->fid.unique;
4058     cfid = scp->fid;
4059
4060     osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4061
4062     reqp->flags |= CM_REQ_NORETRY;
4063     lock_ReleaseWrite(&scp->rw);
4064
4065     do {
4066         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4067         if (code) 
4068             break;
4069
4070         rxconnp = cm_GetRxConn(connp);
4071         code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4072                              &volSync);
4073         rx_PutConnection(rxconnp);
4074
4075     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4076                         NULL, NULL, code));
4077
4078     code = cm_MapRPCError(code, reqp);
4079     if (code) {
4080         osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4081     } else {
4082         osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4083     }
4084
4085     lock_ObtainWrite(&scp->rw);
4086     reqp->flags = reqflags;
4087     return code;
4088 }
4089
4090 /* called with scp->rw held.  Releases it during processing */
4091 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4092                        cm_req_t * reqp) {
4093     long code = 0;
4094     AFSFid tfid;
4095     cm_fid_t cfid;
4096     cm_conn_t * connp;
4097     struct rx_connection * rxconnp;
4098     AFSVolSync volSync;
4099
4100     tfid.Volume = scp->fid.volume;
4101     tfid.Vnode = scp->fid.vnode;
4102     tfid.Unique = scp->fid.unique;
4103     cfid = scp->fid;
4104
4105     lock_ReleaseWrite(&scp->rw);
4106
4107     osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4108
4109     do {
4110         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4111         if (code) 
4112             break;
4113
4114         rxconnp = cm_GetRxConn(connp);
4115         code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4116         rx_PutConnection(rxconnp);
4117
4118     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4119                         NULL, NULL, code));
4120     code = cm_MapRPCError(code, reqp);
4121     if (code)
4122         osi_Log1(afsd_logp,
4123                  "CALL ReleaseLock FAILURE, code 0x%x", code);
4124     else
4125         osi_Log0(afsd_logp,
4126                  "CALL ReleaseLock SUCCESS");
4127         
4128     lock_ObtainWrite(&scp->rw);
4129
4130     return code;
4131 }
4132
4133 /* called with scp->rw held.  May release it during processing, but
4134    will exit with lock held.
4135
4136    This will return:
4137
4138    - 0 if the user has permission to get the specified lock for the scp
4139
4140    - CM_ERROR_NOACCESS if not
4141
4142    Any other error from cm_SyncOp will be sent down untranslated.
4143
4144    If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4145    phas_insert (if non-NULL) will receive a boolean value indicating
4146    whether the user has INSERT permission or not.
4147 */
4148 long cm_LockCheckPerms(cm_scache_t * scp,
4149                        int lock_type,
4150                        cm_user_t * userp,
4151                        cm_req_t * reqp,
4152                        int * phas_insert)
4153 {
4154     long rights = 0;
4155     long code = 0, code2 = 0;
4156
4157     /* lock permissions are slightly tricky because of the 'i' bit.
4158        If the user has PRSFS_LOCK, she can read-lock the file.  If the
4159        user has PRSFS_WRITE, she can write-lock the file.  However, if
4160        the user has PRSFS_INSERT, then she can write-lock new files,
4161        but not old ones.  Since we don't have information about
4162        whether a file is new or not, we assume that if the user owns
4163        the scp, then she has the permissions that are granted by
4164        PRSFS_INSERT. */
4165
4166     osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4167              scp, lock_type, userp);
4168
4169     if (lock_type == LockRead)
4170         rights |= PRSFS_LOCK;
4171     else if (lock_type == LockWrite)
4172         rights |= PRSFS_WRITE | PRSFS_LOCK;
4173     else {
4174         /* hmmkay */
4175         osi_assertx(FALSE, "invalid lock type");
4176         return 0;
4177     }
4178
4179     if (phas_insert)
4180         *phas_insert = FALSE;
4181
4182     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4183                      CM_SCACHESYNC_GETSTATUS |
4184                      CM_SCACHESYNC_NEEDCALLBACK);
4185
4186     if (phas_insert && scp->creator == userp) {
4187
4188         /* If this file was created by the user, then we check for
4189            PRSFS_INSERT.  If the file server is recent enough, then
4190            this should be sufficient for her to get a write-lock (but
4191            not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4192            indicates whether a file server supports getting write
4193            locks when the user only has PRSFS_INSERT. 
4194            
4195            If the file was not created by the user we skip the check
4196            because the INSERT bit will not apply to this user even
4197            if it is set.
4198          */
4199
4200         code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4201                          CM_SCACHESYNC_GETSTATUS |
4202                          CM_SCACHESYNC_NEEDCALLBACK);
4203
4204         if (code2 == CM_ERROR_NOACCESS) {
4205             osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4206         } else {
4207             *phas_insert = TRUE;
4208             osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4209         }
4210     }
4211
4212     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4213
4214     osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4215
4216     return code;
4217 }
4218
4219 /* called with scp->rw held */
4220 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4221              LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4222              cm_key_t key,
4223              int allowWait, cm_user_t *userp, cm_req_t *reqp,
4224              cm_file_lock_t **lockpp)
4225 {
4226     long code = 0;
4227     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4228     cm_file_lock_t *fileLock;
4229     osi_queue_t *q;
4230     cm_range_t range;
4231     int wait_unlock = FALSE;
4232     int force_client_lock = FALSE;
4233
4234     osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4235              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4236     osi_Log3(afsd_logp, "... allowWait %d key 0x%x:%x", allowWait, 
4237              (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
4238
4239     /*
4240    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4241
4242    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4243       true:
4244
4245        3.1 If L->LockType is exclusive then there does NOT exist a
4246          ACCEPTED lock M in S->fileLocks such that _a_ in
4247          (M->LOffset,+M->LLength).
4248
4249        3.2 If L->LockType is shared then for each ACCEPTED lock M in
4250          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4251          M->LockType is shared.
4252
4253    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4254
4255        4.1 M->key != Key(C)
4256
4257        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4258          and (M->LOffset,+M->LLength) do not intersect.
4259     */
4260
4261     range.offset = LOffset.QuadPart;
4262     range.length = LLength.QuadPart;
4263
4264     lock_ObtainRead(&cm_scacheLock);
4265
4266     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4267         fileLock =
4268             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4269
4270         if (IS_LOCK_LOST(fileLock)) {
4271             if (fileLock->key == key) {
4272                 code = CM_ERROR_BADFD;
4273                 break;
4274             } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4275                 code = CM_ERROR_WOULDBLOCK;
4276                 wait_unlock = TRUE;
4277                 break;
4278             }
4279         }
4280
4281         /* we don't need to check for deleted locks here since deleted
4282            locks are dequeued from scp->fileLocks */
4283         if (IS_LOCK_ACCEPTED(fileLock) &&
4284            INTERSECT_RANGE(range, fileLock->range)) {
4285
4286             if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4287                 fileLock->lockType != LockRead) {
4288                 wait_unlock = TRUE;
4289                 code = CM_ERROR_WOULDBLOCK;
4290                 break;
4291             }
4292         }
4293     }
4294
4295     lock_ReleaseRead(&cm_scacheLock);
4296
4297     if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4298         if (Which == scp->serverLock ||
4299            (Which == LockRead && scp->serverLock == LockWrite)) {
4300
4301             int has_insert = 0;
4302
4303             /* we already have the lock we need */
4304             osi_Log3(afsd_logp, "   we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]", 
4305                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4306
4307             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4308
4309             /* special case: if we don't have permission to read-lock
4310                the file, then we force a clientside lock.  This is to
4311                compensate for applications that obtain a read-lock for
4312                reading files off of directories that don't grant
4313                read-locks to the user. */
4314             if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4315
4316                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4317                     osi_Log0(afsd_logp, "   User has no read-lock perms, but has INSERT perms.");
4318                     code = 0;
4319                 } else {
4320                     osi_Log0(afsd_logp, "   User has no read-lock perms. Forcing client-side lock");
4321                     force_client_lock = TRUE;
4322                 }
4323             }
4324
4325         } else if ((scp->exclusiveLocks > 0) ||
4326                    (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4327             int has_insert = 0;
4328
4329             /* We are already waiting for some other lock.  We should
4330                wait for the daemon to catch up instead of generating a
4331                flood of SetLock calls. */
4332             osi_Log3(afsd_logp, "   already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4333                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4334
4335             /* see if we have permission to create the lock in the
4336                first place. */
4337             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4338             if (code == 0)
4339                 code = CM_ERROR_WOULDBLOCK;
4340             else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4341
4342                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4343                     osi_Log0(afsd_logp,
4344                              "   User has no read-lock perms, but has INSERT perms.");
4345                     code = CM_ERROR_WOULDBLOCK;
4346                 } else {
4347                     osi_Log0(afsd_logp,
4348                              "   User has no read-lock perms. Forcing client-side lock");
4349                     force_client_lock = TRUE;
4350                 }
4351             }
4352
4353             /* leave any other codes as-is */
4354
4355         } else {
4356             int newLock;
4357             int check_data_version = FALSE;
4358             int has_insert = 0;
4359
4360             /* first check if we have permission to elevate or obtain
4361                the lock. */
4362             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4363             if (code) {
4364                 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4365                     (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4366                     osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
4367                     force_client_lock = TRUE;
4368                 }
4369                 goto check_code;
4370             }
4371
4372             /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4373
4374             if (scp->serverLock == LockRead && Which == LockWrite) {
4375
4376                 /* We want to escalate the lock to a LockWrite.
4377                  * Unfortunately that's not really possible without
4378                  * letting go of the current lock.  But for now we do
4379                  * it anyway. */
4380
4381                 osi_Log0(afsd_logp,
4382                          "   attempting to UPGRADE from LockRead to LockWrite.");
4383                 osi_Log1(afsd_logp,
4384                          "   dataVersion on scp: %I64d", scp->dataVersion);
4385
4386                 /* we assume at this point (because scp->serverLock
4387                    was valid) that we had a valid server lock. */
4388                 scp->lockDataVersion = scp->dataVersion;
4389                 check_data_version = TRUE;
4390         
4391                 code = cm_IntReleaseLock(scp, userp, reqp);
4392
4393                 if (code) {
4394                     /* We couldn't release the lock */
4395                     goto check_code;
4396                 } else {
4397                     scp->serverLock = -1;
4398                 }
4399             }
4400
4401             /* We need to obtain a server lock of type Which in order
4402              * to assert this file lock */
4403 #ifndef AGGRESSIVE_LOCKS
4404             newLock = Which;
4405 #else
4406             newLock = LockWrite;
4407 #endif
4408
4409             code = cm_IntSetLock(scp, userp, newLock, reqp);
4410
4411 #ifdef AGGRESSIVE_LOCKS
4412             if ((code == CM_ERROR_WOULDBLOCK ||
4413                  code == CM_ERROR_NOACCESS) && newLock != Which) {
4414                 /* we wanted LockRead.  We tried LockWrite. Now try
4415                  * LockRead again */
4416                 newLock = Which;
4417
4418                 /* am I sane? */
4419                 osi_assertx(newLock == LockRead, "lock type not read");
4420
4421                 code = cm_IntSetLock(scp, userp, newLock, reqp);
4422             }
4423 #endif
4424
4425             if (code == CM_ERROR_NOACCESS) {
4426                 if (Which == LockRead) {
4427                     if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4428                         long tcode;
4429                         /* We requested a read-lock, but we have permission to
4430                          * get a write-lock. Try that */
4431
4432                         tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4433
4434                         if (tcode == 0) {
4435                             newLock = LockWrite;
4436
4437                             osi_Log0(afsd_logp, "   User has 'i' perms and the request was for a LockRead.  Trying to get a LockWrite instead");
4438
4439                             code = cm_IntSetLock(scp, userp, newLock, reqp);
4440                         }
4441                     } else {
4442                         osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
4443                         force_client_lock = TRUE;
4444                     }
4445                 } else if (Which == LockWrite &&
4446                            scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4447                     long tcode;
4448
4449                     /* Special case: if the lock request was for a
4450                      * LockWrite and the user owns the file and we weren't
4451                      * allowed to obtain the serverlock, we either lost a
4452                      * race (the permissions changed from under us), or we
4453                      * have 'i' bits, but we aren't allowed to lock the
4454                      * file. */
4455
4456                     /* check if we lost a race... */
4457                     tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4458
4459                     if (tcode == 0) {
4460                         osi_Log0(afsd_logp, "   User has 'i' perms but can't obtain write locks. Using client-side locks.");
4461                         force_client_lock = TRUE;
4462                     }
4463                 }
4464             }
4465
4466             if (code == 0 && check_data_version &&
4467                scp->dataVersion != scp->lockDataVersion) {
4468                 /* We lost a race.  Although we successfully obtained
4469                  * a lock, someone modified the file in between.  The
4470                  * locks have all been technically lost. */
4471
4472                 osi_Log0(afsd_logp,
4473                          "  Data version mismatch while upgrading lock.");
4474                 osi_Log2(afsd_logp,
4475                          "  Data versions before=%I64d, after=%I64d",
4476                          scp->lockDataVersion,
4477                          scp->dataVersion);
4478                 osi_Log1(afsd_logp,
4479                          "  Releasing stale lock for scp 0x%x", scp);
4480
4481                 code = cm_IntReleaseLock(scp, userp, reqp);
4482
4483                 scp->serverLock = -1;
4484
4485                 code = CM_ERROR_INVAL;
4486             } else if (code == 0) {
4487                 scp->serverLock = newLock;
4488                 scp->lockDataVersion = scp->dataVersion;
4489             }
4490
4491             if (code != 0 &&
4492                 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4493                 scp->serverLock == -1) {
4494                 /* Oops. We lost the lock. */
4495                 cm_LockMarkSCacheLost(scp);
4496             }
4497         }
4498     } else if (code == 0) {     /* server locks not enabled */
4499         osi_Log0(afsd_logp,
4500                  "  Skipping server lock for scp");
4501     }
4502
4503  check_code:
4504
4505     if (code != 0 && !force_client_lock) {
4506         /* Special case error translations
4507
4508            Applications don't expect certain errors from a
4509            LockFile/UnlockFile call.  We need to translate some error
4510            code to codes that apps expect and handle. */
4511
4512         /* We shouldn't actually need to handle this case since we
4513            simulate locks for RO scps anyway. */
4514         if (code == CM_ERROR_READONLY) {
4515             osi_Log0(afsd_logp, "   Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4516             code = CM_ERROR_NOACCESS;
4517         }
4518     }
4519
4520     if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4521         force_client_lock) {
4522
4523         /* clear the error if we are forcing a client lock, so we
4524            don't get confused later. */
4525         if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4526             code = 0;
4527
4528         lock_ObtainWrite(&cm_scacheLock);
4529         fileLock = cm_GetFileLock();
4530         lock_ReleaseWrite(&cm_scacheLock);
4531 #ifdef DEBUG
4532         fileLock->fid = scp->fid;
4533 #endif
4534         fileLock->key = key;
4535         fileLock->lockType = Which;
4536         cm_HoldUser(userp);
4537         fileLock->userp = userp;
4538         fileLock->range = range;
4539         fileLock->flags = (code == 0 ? 0 : 
4540                            ((wait_unlock)?
4541                             CM_FILELOCK_FLAG_WAITUNLOCK :
4542                             CM_FILELOCK_FLAG_WAITLOCK));
4543
4544         if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4545             fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4546
4547         fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4548
4549         lock_ObtainWrite(&cm_scacheLock);
4550         osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4551         cm_HoldSCacheNoLock(scp);
4552         fileLock->scp = scp;
4553         osi_QAdd(&cm_allFileLocks, &fileLock->q);
4554         lock_ReleaseWrite(&cm_scacheLock);
4555
4556         if (code != 0) {
4557             *lockpp = fileLock;
4558         }
4559
4560         if (IS_LOCK_CLIENTONLY(fileLock)) {
4561             scp->clientLocks++;
4562         } else if (IS_LOCK_ACCEPTED(fileLock)) {
4563             if (Which == LockRead)
4564                 scp->sharedLocks++;
4565             else
4566                 scp->exclusiveLocks++;
4567         }
4568
4569         osi_Log3(afsd_logp,
4570                  "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4571                  fileLock, fileLock->flags, scp);
4572         osi_Log4(afsd_logp,
4573                  "   exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4574                  scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4575                  (int)(signed char) scp->serverLock);
4576     } else {
4577         osi_Log1(afsd_logp,
4578                  "cm_Lock Rejecting lock (code = 0x%x)", code);
4579     }
4580
4581     return code;
4582 }
4583
4584 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
4585
4586 /* Called with scp->rw held */
4587 long cm_UnlockByKey(cm_scache_t * scp,
4588                     cm_key_t key,
4589                     int flags,
4590                     cm_user_t * userp,
4591                      cm_req_t * reqp)
4592 {
4593     long code = 0;
4594     cm_file_lock_t *fileLock;
4595     osi_queue_t *q, *qn;
4596     int n_unlocks = 0;
4597
4598     osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key 0x%x:%x flags=0x%x",
4599              scp,
4600              (unsigned long)(key >> 32),
4601              (unsigned long)(key & 0xffffffff),
4602              flags);
4603
4604     lock_ObtainWrite(&cm_scacheLock);
4605
4606     for (q = scp->fileLocksH; q; q = qn) {
4607         qn = osi_QNext(q);
4608
4609         fileLock = (cm_file_lock_t *)
4610             ((char *) q - offsetof(cm_file_lock_t, fileq));
4611
4612 #ifdef DEBUG
4613         osi_Log4(afsd_logp, "   Checking lock[0x%x] range[%d,+%d] type[%d]",
4614                  fileLock,
4615                  (unsigned long) fileLock->range.offset,
4616                  (unsigned long) fileLock->range.length,
4617                 fileLock->lockType);
4618         osi_Log3(afsd_logp, "     key[0x%x:%x] flags[0x%x]",
4619                  (unsigned long)(fileLock->key >> 32),
4620                  (unsigned long)(fileLock->key & 0xffffffff),
4621                  fileLock->flags);
4622
4623         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4624             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4625             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4626                      fileLock->fid.cell,
4627                      fileLock->fid.volume,
4628                      fileLock->fid.vnode,
4629                      fileLock->fid.unique);
4630             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4631                      fileLock->scp->fid.cell,
4632                      fileLock->scp->fid.volume,
4633                      fileLock->scp->fid.vnode,
4634                      fileLock->scp->fid.unique);
4635             osi_assertx(FALSE, "invalid fid value");
4636         }
4637 #endif
4638
4639         if (!IS_LOCK_DELETED(fileLock) &&
4640             cm_KeyEquals(fileLock->key, key, flags)) {
4641             osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4642                     fileLock->range.offset,
4643                     fileLock->range.length,
4644                     fileLock->lockType);
4645
4646             if (scp->fileLocksT == q)
4647                 scp->fileLocksT = osi_QPrev(q);
4648             osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4649
4650             if (IS_LOCK_CLIENTONLY(fileLock)) {
4651                 scp->clientLocks--;
4652             } else if (IS_LOCK_ACCEPTED(fileLock)) {
4653                 if (fileLock->lockType == LockRead)
4654                     scp->sharedLocks--;
4655                 else
4656                     scp->exclusiveLocks--;
4657             }
4658
4659             fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4660
4661             cm_ReleaseUser(fileLock->userp);
4662             cm_ReleaseSCacheNoLock(scp);
4663
4664             fileLock->userp = NULL;
4665             fileLock->scp = NULL;
4666
4667             n_unlocks++;
4668         }
4669     }
4670
4671     lock_ReleaseWrite(&cm_scacheLock);
4672
4673     if (n_unlocks == 0) {
4674         osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4675         osi_Log3(afsd_logp, "   Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4676                  scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4677         
4678         return 0;
4679     }
4680
4681     osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4682
4683     osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4684     osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4685     osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4686
4687     if (!SERVERLOCKS_ENABLED(scp)) {
4688         osi_Log0(afsd_logp, "  Skipping server lock for scp");
4689         goto done;
4690     }
4691
4692     /* Ideally we would go through the rest of the locks to determine
4693      * if one or more locks that were formerly in WAITUNLOCK can now
4694      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4695      * scp->sharedLocks accordingly.  However, the retrying of locks
4696      * in that manner is done cm_RetryLock() manually.
4697      */
4698
4699     if (scp->serverLock == LockWrite &&
4700         scp->exclusiveLocks == 0 &&
4701         scp->sharedLocks > 0) {
4702
4703         /* The serverLock should be downgraded to LockRead */
4704         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
4705
4706         /* since scp->serverLock looked sane, we are going to assume
4707            that we have a valid server lock. */
4708         scp->lockDataVersion = scp->dataVersion;
4709         osi_Log1(afsd_logp, "  dataVersion on scp = %I64d", scp->dataVersion);
4710
4711         code = cm_IntReleaseLock(scp, userp, reqp);
4712
4713         if (code) {
4714             /* so we couldn't release it.  Just let the lock be for now */
4715             code = 0;
4716             goto done;
4717         } else {
4718             scp->serverLock = -1;
4719         }
4720
4721         code = cm_IntSetLock(scp, userp, LockRead, reqp);
4722
4723         if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4724             scp->serverLock = LockRead;
4725         } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4726             /* We lost a race condition.  Although we have a valid
4727                lock on the file, the data has changed and essentially
4728                we have lost the lock we had during the transition. */
4729
4730             osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4731             osi_Log2(afsd_logp, "  Data versions before=%I64d, after=%I64d",
4732                      scp->lockDataVersion,
4733                      scp->dataVersion);
4734             
4735             code = cm_IntReleaseLock(scp, userp, reqp);
4736
4737             code = CM_ERROR_INVAL;
4738             scp->serverLock = -1;
4739         }
4740
4741         if (code != 0 &&
4742             (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4743                 (scp->serverLock == -1)) {
4744                 /* Oopsie */
4745                 cm_LockMarkSCacheLost(scp);
4746             }
4747
4748         /* failure here has no bearing on the return value of
4749            cm_Unlock() */
4750         code = 0;
4751
4752     } else if (scp->serverLock != (-1) &&
4753               scp->exclusiveLocks == 0 &&
4754               scp->sharedLocks == 0) {
4755         /* The serverLock should be released entirely */
4756
4757         code = cm_IntReleaseLock(scp, userp, reqp);
4758
4759         if (code == 0)
4760             scp->serverLock = (-1);
4761     }
4762
4763  done:
4764
4765     osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4766     osi_Log4(afsd_logp, "   Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4767              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4768              (int)(signed char) scp->serverLock);
4769
4770     return code;
4771 }
4772
4773 long cm_Unlock(cm_scache_t *scp, 
4774                unsigned char sLockType,
4775                LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4776                cm_key_t key, 
4777                cm_user_t *userp, 
4778                cm_req_t *reqp)
4779 {
4780     long code = 0;
4781     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4782     cm_file_lock_t *fileLock;
4783     osi_queue_t *q;
4784     int release_userp = FALSE;
4785
4786     osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4787              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4788     osi_Log2(afsd_logp, "... key 0x%x:%x",
4789              (unsigned long) (key >> 32), (unsigned long) (key & 0xffffffff));
4790
4791     lock_ObtainRead(&cm_scacheLock);
4792
4793     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4794         fileLock = (cm_file_lock_t *)
4795             ((char *) q - offsetof(cm_file_lock_t, fileq));
4796
4797 #ifdef DEBUG
4798         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4799             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4800             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4801                      fileLock->fid.cell,
4802                      fileLock->fid.volume,
4803                      fileLock->fid.vnode,
4804                      fileLock->fid.unique);
4805             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4806                      fileLock->scp->fid.cell,
4807                      fileLock->scp->fid.volume,
4808                      fileLock->scp->fid.vnode,
4809                      fileLock->scp->fid.unique);
4810             osi_assertx(FALSE, "invalid fid value");
4811         }
4812 #endif
4813         if (!IS_LOCK_DELETED(fileLock) &&
4814             fileLock->key == key &&
4815             fileLock->range.offset == LOffset.QuadPart &&
4816             fileLock->range.length == LLength.QuadPart) {
4817             break;
4818         }
4819     }
4820
4821     if (!q) {
4822         osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
4823         
4824         lock_ReleaseRead(&cm_scacheLock);
4825
4826         /* The lock didn't exist anyway. *shrug* */
4827         return CM_ERROR_RANGE_NOT_LOCKED;
4828     }
4829
4830     /* discard lock record */
4831     lock_ConvertRToW(&cm_scacheLock);
4832     if (scp->fileLocksT == q)
4833         scp->fileLocksT = osi_QPrev(q);
4834     osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4835
4836     /*
4837      * Don't delete it here; let the daemon delete it, to simplify
4838      * the daemon's traversal of the list.
4839      */
4840
4841     if (IS_LOCK_CLIENTONLY(fileLock)) {
4842         scp->clientLocks--;
4843     } else if (IS_LOCK_ACCEPTED(fileLock)) {
4844         if (fileLock->lockType == LockRead)
4845             scp->sharedLocks--;
4846         else
4847             scp->exclusiveLocks--;
4848     }
4849
4850     fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4851     if (userp != NULL) {
4852         cm_ReleaseUser(fileLock->userp);
4853     } else {
4854         userp = fileLock->userp;
4855         release_userp = TRUE;
4856     }
4857     fileLock->userp = NULL;
4858     cm_ReleaseSCacheNoLock(scp);
4859     fileLock->scp = NULL;
4860     lock_ReleaseWrite(&cm_scacheLock);
4861
4862     if (!SERVERLOCKS_ENABLED(scp)) {
4863         osi_Log0(afsd_logp, "   Skipping server locks for scp");
4864         goto done;
4865     }
4866
4867     /* Ideally we would go through the rest of the locks to determine
4868      * if one or more locks that were formerly in WAITUNLOCK can now
4869      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4870      * scp->sharedLocks accordingly.  However, the retrying of locks
4871      * in that manner is done cm_RetryLock() manually.
4872      */
4873
4874     if (scp->serverLock == LockWrite &&
4875         scp->exclusiveLocks == 0 &&
4876         scp->sharedLocks > 0) {
4877
4878         /* The serverLock should be downgraded to LockRead */
4879         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
4880
4881         /* Since we already had a lock, we assume that there is a
4882            valid server lock. */
4883         scp->lockDataVersion = scp->dataVersion;
4884         osi_Log1(afsd_logp, "   dataVersion on scp is %I64d", scp->dataVersion);
4885
4886         /* before we downgrade, make sure that we have enough
4887            permissions to get the read lock. */
4888         code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
4889         if (code != 0) {
4890
4891             osi_Log0(afsd_logp, "  SKIPPING downgrade because user doesn't have perms to get downgraded lock");
4892
4893             code = 0;
4894             goto done;
4895         }
4896
4897         code = cm_IntReleaseLock(scp, userp, reqp);
4898
4899         if (code) {
4900             /* so we couldn't release it.  Just let the lock be for now */
4901             code = 0;
4902             goto done;
4903         } else {
4904             scp->serverLock = -1;
4905         }
4906
4907         code = cm_IntSetLock(scp, userp, LockRead, reqp);
4908
4909         if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4910             scp->serverLock = LockRead;
4911         } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4912             /* Lost a race.  We obtained a new lock, but that is
4913                meaningless since someone modified the file
4914                inbetween. */
4915
4916             osi_Log0(afsd_logp,
4917                      "Data version mismatch while downgrading lock");
4918             osi_Log2(afsd_logp,
4919                      "  Data versions before=%I64d, after=%I64d",
4920                      scp->lockDataVersion,
4921                      scp->dataVersion);
4922             
4923             code = cm_IntReleaseLock(scp, userp, reqp);
4924
4925             scp->serverLock = -1;
4926             code = CM_ERROR_INVAL;
4927         }
4928
4929         if (code != 0 &&
4930             (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4931                 (scp->serverLock == -1)) {
4932                 /* Oopsie */
4933                 cm_LockMarkSCacheLost(scp);
4934             }
4935
4936         /* failure here has no bearing on the return value of
4937            cm_Unlock() */
4938         code = 0;
4939
4940     } else if (scp->serverLock != (-1) &&
4941               scp->exclusiveLocks == 0 &&
4942               scp->sharedLocks == 0) {
4943         /* The serverLock should be released entirely */
4944
4945         code = cm_IntReleaseLock(scp, userp, reqp);
4946
4947         if (code == 0) {
4948             scp->serverLock = (-1);
4949         }
4950     }
4951
4952     if (release_userp)
4953         cm_ReleaseUser(userp);
4954
4955  done:
4956
4957     osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
4958     osi_Log4(afsd_logp, "  leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4959              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4960              (int)(signed char) scp->serverLock);
4961
4962     return code;
4963 }
4964
4965 /* called with scp->rw held */
4966 void cm_LockMarkSCacheLost(cm_scache_t * scp)
4967 {
4968     cm_file_lock_t *fileLock;
4969     osi_queue_t *q;
4970
4971     osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
4972
4973     /* cm_scacheLock needed because we are modifying fileLock->flags */
4974     lock_ObtainWrite(&cm_scacheLock);
4975
4976     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4977         fileLock = 
4978             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4979
4980         if (IS_LOCK_ACTIVE(fileLock) &&
4981             !IS_LOCK_CLIENTONLY(fileLock)) {
4982             if (fileLock->lockType == LockRead)
4983                 scp->sharedLocks--;
4984             else
4985                 scp->exclusiveLocks--;
4986
4987             fileLock->flags |= CM_FILELOCK_FLAG_LOST;
4988         }
4989     }
4990
4991     scp->serverLock = -1;
4992     scp->lockDataVersion = -1;
4993     lock_ReleaseWrite(&cm_scacheLock);
4994 }
4995
4996 /* Called with no relevant locks held */
4997 void cm_CheckLocks()
4998 {
4999     osi_queue_t *q, *nq;
5000     cm_file_lock_t *fileLock;
5001     cm_req_t req;
5002     AFSFid tfid;
5003     AFSVolSync volSync;
5004     cm_conn_t *connp;
5005     long code;
5006     struct rx_connection * rxconnp;
5007     cm_scache_t * scp;
5008
5009     cm_InitReq(&req);
5010
5011     lock_ObtainWrite(&cm_scacheLock);
5012
5013     cm_lockRefreshCycle++;
5014
5015     osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5016
5017     for (q = cm_allFileLocks; q; q = nq) {
5018         fileLock = (cm_file_lock_t *) q;
5019         nq = osi_QNext(q);
5020         code = -1;
5021
5022         if (IS_LOCK_DELETED(fileLock)) {
5023
5024             osi_QRemove(&cm_allFileLocks, q);
5025             cm_PutFileLock(fileLock);
5026
5027         } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5028
5029             /* Server locks must have been enabled for us to have
5030                received an active non-client-only lock. */
5031             osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5032
5033             scp = fileLock->scp;
5034             osi_assertx(scp != NULL, "null cm_scache_t");
5035
5036             cm_HoldSCacheNoLock(scp);
5037
5038 #ifdef DEBUG
5039             if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5040                 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5041                 osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5042                          fileLock->fid.cell,
5043                          fileLock->fid.volume,
5044                          fileLock->fid.vnode,
5045                          fileLock->fid.unique);
5046                 osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5047                          fileLock->scp->fid.cell,
5048                          fileLock->scp->fid.volume,
5049                          fileLock->scp->fid.vnode,
5050                          fileLock->scp->fid.unique);
5051                 osi_assertx(FALSE, "invalid fid");
5052             }
5053 #endif
5054             /* Server locks are extended once per scp per refresh
5055                cycle. */
5056             if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5057
5058                 int scp_done = FALSE;
5059
5060                 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5061
5062                 lock_ReleaseWrite(&cm_scacheLock);
5063                 lock_ObtainWrite(&scp->rw);
5064
5065                 /* did the lock change while we weren't holding the lock? */
5066                 if (!IS_LOCK_ACTIVE(fileLock))
5067                     goto post_syncopdone;
5068
5069                 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5070                                  CM_SCACHESYNC_NEEDCALLBACK
5071                                  | CM_SCACHESYNC_GETSTATUS
5072                                  | CM_SCACHESYNC_LOCK);
5073
5074                 if (code) {
5075                     osi_Log1(afsd_logp,
5076                              "cm_CheckLocks SyncOp failure code 0x%x", code);
5077                     goto post_syncopdone;
5078                 }
5079
5080                 /* cm_SyncOp releases scp->rw during which the lock
5081                    may get released. */
5082                 if (!IS_LOCK_ACTIVE(fileLock))
5083                     goto pre_syncopdone;
5084
5085                 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5086                     cm_fid_t cfid;
5087                     cm_user_t * userp;
5088
5089                     tfid.Volume = scp->fid.volume;
5090                     tfid.Vnode = scp->fid.vnode;
5091                     tfid.Unique = scp->fid.unique;
5092                     cfid = scp->fid;
5093                     userp = fileLock->userp;
5094                     
5095                     osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d", 
5096                              fileLock,
5097                              scp,
5098                              (int) scp->serverLock);
5099
5100                     lock_ReleaseWrite(&scp->rw);
5101
5102                     do {
5103                         code = cm_ConnFromFID(&cfid, userp,
5104                                        &req, &connp);
5105                         if (code) 
5106                             break;
5107
5108                         rxconnp = cm_GetRxConn(connp);
5109                         code = RXAFS_ExtendLock(rxconnp, &tfid,
5110                                                 &volSync);
5111                         rx_PutConnection(rxconnp);
5112
5113                         osi_Log1(afsd_logp, "   ExtendLock returns %d", code);
5114
5115                     } while (cm_Analyze(connp, userp, &req,
5116                                         &cfid, &volSync, NULL, NULL,
5117                                         code));
5118
5119                     code = cm_MapRPCError(code, &req);
5120
5121                     lock_ObtainWrite(&scp->rw);
5122
5123                     if (code) {
5124                         osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5125                     } else {
5126                         osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5127                         scp->lockDataVersion = scp->dataVersion;
5128                     }
5129
5130                     if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5131                         scp->lockDataVersion == scp->dataVersion) {
5132                         int lockType;
5133
5134                         lockType =
5135                             (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5136
5137                         /* we might still have a chance to obtain a
5138                            new lock */
5139
5140                         code = cm_IntSetLock(scp, userp, lockType, &req);
5141
5142                         if (code) {
5143                             code = CM_ERROR_INVAL;
5144                         } else if (scp->lockDataVersion != scp->dataVersion) {
5145
5146                             /* now check if we still have the file at
5147                                the right data version. */
5148                             osi_Log1(afsd_logp,
5149                                      "Data version mismatch on scp 0x%p",
5150                                      scp);
5151                             osi_Log2(afsd_logp,
5152                                      "   Data versions: before=%I64d, after=%I64d",
5153                                      scp->lockDataVersion,
5154                                      scp->dataVersion);
5155
5156                             code = cm_IntReleaseLock(scp, userp, &req);
5157
5158                             code = CM_ERROR_INVAL;
5159                         }
5160                     }
5161
5162                     if (code == EINVAL || code == CM_ERROR_INVAL ||
5163                         code == CM_ERROR_BADFD) {
5164                         cm_LockMarkSCacheLost(scp);
5165                     }
5166
5167                 } else {
5168                     /* interestingly, we have found an active lock
5169                        belonging to an scache that has no
5170                        serverLock */
5171                     cm_LockMarkSCacheLost(scp);
5172                 }
5173
5174                 scp_done = TRUE;
5175
5176             pre_syncopdone:
5177
5178                 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5179
5180             post_syncopdone:
5181                 lock_ReleaseWrite(&scp->rw);
5182
5183                 lock_ObtainWrite(&cm_scacheLock);
5184
5185                 if (code == 0) {
5186                     fileLock->lastUpdate = time(NULL);
5187                 }
5188                 
5189                 if (scp_done)
5190                     scp->lastRefreshCycle = cm_lockRefreshCycle;
5191
5192             } else {
5193                 /* we have already refreshed the locks on this scp */
5194                 fileLock->lastUpdate = time(NULL);
5195             }
5196
5197             cm_ReleaseSCacheNoLock(scp);
5198
5199         } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5200             /* TODO: Check callbacks */
5201         }
5202     }
5203
5204     lock_ReleaseWrite(&cm_scacheLock);
5205     osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5206 }
5207
5208 /* NOT called with scp->rw held. */
5209 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5210 {
5211     long code = 0;
5212     cm_scache_t *scp = NULL;
5213     cm_file_lock_t *fileLock;
5214     osi_queue_t *q;
5215     cm_req_t req;
5216     int newLock = -1;
5217     int force_client_lock = FALSE;
5218     int has_insert = FALSE;
5219     int check_data_version = FALSE;
5220
5221     cm_InitReq(&req);
5222
5223     if (client_is_dead) {
5224         code = CM_ERROR_TIMEDOUT;
5225         goto updateLock;
5226     }
5227
5228     lock_ObtainRead(&cm_scacheLock);
5229
5230     osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5231     osi_Log4(afsd_logp, "    offset(%x:%x) length(%x:%x)",
5232              (unsigned)(oldFileLock->range.offset >> 32),
5233              (unsigned)(oldFileLock->range.offset & 0xffffffff),
5234              (unsigned)(oldFileLock->range.length >> 32),
5235              (unsigned)(oldFileLock->range.length & 0xffffffff));
5236     osi_Log3(afsd_logp, "    key(%x:%x) flags=%x",
5237              (unsigned)(oldFileLock->key >> 32),
5238              (unsigned)(oldFileLock->key & 0xffffffff),
5239              (unsigned)(oldFileLock->flags));
5240
5241     /* if the lock has already been granted, then we have nothing to do */
5242     if (IS_LOCK_ACTIVE(oldFileLock)) {
5243         lock_ReleaseRead(&cm_scacheLock);
5244         osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5245         return 0;
5246     }
5247
5248     /* we can't do anything with lost or deleted locks at the moment. */
5249     if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5250         code = CM_ERROR_BADFD;
5251         osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5252         lock_ReleaseRead(&cm_scacheLock);
5253         goto updateLock;
5254     }
5255
5256     scp = oldFileLock->scp;
5257
5258     osi_assertx(scp != NULL, "null cm_scache_t");
5259
5260     lock_ReleaseRead(&cm_scacheLock);
5261     lock_ObtainWrite(&scp->rw);
5262
5263     code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5264                              oldFileLock->userp,
5265                              &req, &has_insert);
5266
5267     if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5268         if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5269         force_client_lock = TRUE;
5270         }
5271         code = 0;
5272     } else if (code) {
5273         lock_ReleaseWrite(&scp->rw);
5274         return code;
5275     }
5276
5277     lock_ObtainWrite(&cm_scacheLock);
5278
5279     /* Check if we already have a sufficient server lock to allow this
5280        lock to go through. */
5281     if (IS_LOCK_WAITLOCK(oldFileLock) &&
5282         (!SERVERLOCKS_ENABLED(scp) ||
5283          scp->serverLock == oldFileLock->lockType ||
5284          scp->serverLock == LockWrite)) {
5285
5286         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5287
5288         if (SERVERLOCKS_ENABLED(scp)) {
5289             osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock.  Granting",
5290                      (int) scp->serverLock);
5291         } else {
5292             osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5293         }
5294
5295         lock_ReleaseWrite(&cm_scacheLock);
5296         lock_ReleaseWrite(&scp->rw);
5297
5298         return 0;
5299     }
5300
5301     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5302
5303         /* check if the conflicting locks have dissappeared already */
5304         for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5305
5306             fileLock = (cm_file_lock_t *)
5307                 ((char *) q - offsetof(cm_file_lock_t, fileq));
5308
5309             if (IS_LOCK_LOST(fileLock)) {
5310                 if (fileLock->key == oldFileLock->key) {
5311                     code = CM_ERROR_BADFD;
5312                     oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5313                     osi_Log1(afsd_logp, "    found lost lock %p for same key.  Marking lock as lost",
5314                              fileLock);
5315                     break;
5316                 } else if (fileLock->lockType == LockWrite &&
5317                            INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5318                     osi_Log1(afsd_logp, "    found conflicting LOST lock %p", fileLock);
5319                     code = CM_ERROR_WOULDBLOCK;
5320                     break;
5321                 }
5322             }
5323
5324             if (IS_LOCK_ACCEPTED(fileLock) &&
5325                 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5326
5327                 if (oldFileLock->lockType != LockRead ||
5328                    fileLock->lockType != LockRead) {
5329
5330                     osi_Log1(afsd_logp, "    found conflicting lock %p", fileLock);
5331                     code = CM_ERROR_WOULDBLOCK;
5332                     break;
5333                 }
5334             }
5335         }
5336     }
5337
5338     if (code != 0) {
5339         lock_ReleaseWrite(&cm_scacheLock);
5340         lock_ReleaseWrite(&scp->rw);
5341
5342         goto handleCode;
5343     }
5344
5345     /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5346        If it is WAITUNLOCK, then we didn't find any conflicting lock
5347        but we haven't verfied whether the serverLock is sufficient to
5348        assert it.  If it is WAITLOCK, then the serverLock is
5349        insufficient to assert it. Eitherway, we are ready to accept
5350        the lock as either ACTIVE or WAITLOCK depending on the
5351        serverLock. */
5352
5353     /* First, promote the WAITUNLOCK to a WAITLOCK */
5354     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5355         if (oldFileLock->lockType == LockRead)
5356             scp->sharedLocks++;
5357         else
5358             scp->exclusiveLocks++;
5359
5360         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5361         oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5362     }
5363
5364     osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5365
5366     if (force_client_lock ||
5367         !SERVERLOCKS_ENABLED(scp) ||
5368         scp->serverLock == oldFileLock->lockType ||
5369         (oldFileLock->lockType == LockRead &&
5370          scp->serverLock == LockWrite)) {
5371
5372         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5373
5374         if ((force_client_lock ||
5375              !SERVERLOCKS_ENABLED(scp)) &&
5376             !IS_LOCK_CLIENTONLY(oldFileLock)) {
5377
5378             oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5379
5380             if (oldFileLock->lockType == LockRead)
5381                 scp->sharedLocks--;
5382             else
5383                 scp->exclusiveLocks--;
5384
5385             scp->clientLocks++;
5386         }
5387
5388         lock_ReleaseWrite(&cm_scacheLock);
5389         lock_ReleaseWrite(&scp->rw);
5390
5391         return 0;
5392
5393     } else {
5394         cm_user_t * userp;
5395
5396         code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5397                          CM_SCACHESYNC_NEEDCALLBACK
5398                          | CM_SCACHESYNC_GETSTATUS
5399                          | CM_SCACHESYNC_LOCK);
5400         if (code) {
5401             osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5402             lock_ReleaseWrite(&cm_scacheLock);
5403             goto post_syncopdone;
5404         }
5405
5406         if (!IS_LOCK_WAITLOCK(oldFileLock))
5407             goto pre_syncopdone;
5408
5409         userp = oldFileLock->userp;
5410
5411 #ifndef AGGRESSIVE_LOCKS
5412         newLock = oldFileLock->lockType;
5413 #else
5414         newLock = LockWrite;
5415 #endif
5416
5417         if (has_insert) {
5418             /* if has_insert is non-zero, then:
5419                - the lock a LockRead
5420                - we don't have permission to get a LockRead
5421                - we do have permission to get a LockWrite
5422                - the server supports VICED_CAPABILITY_WRITELOCKACL
5423             */
5424
5425             newLock = LockWrite;
5426         }
5427
5428         lock_ReleaseWrite(&cm_scacheLock);
5429
5430         /* when we get here, either we have a read-lock and want a
5431            write-lock or we don't have any locks and we want some
5432            lock. */
5433
5434         if (scp->serverLock == LockRead) {
5435
5436             osi_assertx(newLock == LockWrite, "!LockWrite");
5437
5438             osi_Log0(afsd_logp, "  Attempting to UPGRADE from LockRead to LockWrite");
5439
5440             scp->lockDataVersion = scp->dataVersion;
5441             check_data_version = TRUE;
5442
5443             code = cm_IntReleaseLock(scp, userp, &req);
5444
5445             if (code)
5446                 goto pre_syncopdone;
5447             else
5448                 scp->serverLock = -1;
5449         }
5450
5451         code = cm_IntSetLock(scp, userp, newLock, &req);
5452
5453         if (code == 0) {
5454             if (scp->dataVersion != scp->lockDataVersion) {
5455                 /* we lost a race.  too bad */
5456
5457                 osi_Log0(afsd_logp,
5458                          "  Data version mismatch while upgrading lock.");
5459                 osi_Log2(afsd_logp,
5460                          "  Data versions before=%I64d, after=%I64d",
5461                          scp->lockDataVersion,
5462                          scp->dataVersion);
5463                 osi_Log1(afsd_logp,
5464                          "  Releasing stale lock for scp 0x%x", scp);
5465
5466                 code = cm_IntReleaseLock(scp, userp, &req);
5467
5468                 scp->serverLock = -1;
5469
5470                 code = CM_ERROR_INVAL;
5471
5472                 cm_LockMarkSCacheLost(scp);
5473             } else {
5474                 scp->serverLock = newLock;
5475             }
5476         }
5477
5478     pre_syncopdone:
5479         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5480     post_syncopdone:
5481         ;
5482     }
5483
5484   handleCode:
5485     if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5486         lock_ObtainWrite(&cm_scacheLock);
5487         if (scp->fileLocksT == &oldFileLock->fileq)
5488             scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5489         osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5490         lock_ReleaseWrite(&cm_scacheLock);
5491     }
5492     lock_ReleaseWrite(&scp->rw);
5493
5494   updateLock:
5495     lock_ObtainWrite(&cm_scacheLock);
5496     if (code == 0) {
5497         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5498     } else if (code != CM_ERROR_WOULDBLOCK) {
5499         oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5500         cm_ReleaseUser(oldFileLock->userp);
5501         oldFileLock->userp = NULL;
5502         if (oldFileLock->scp) {
5503             cm_ReleaseSCacheNoLock(oldFileLock->scp);
5504             oldFileLock->scp = NULL;
5505         }
5506     }
5507     lock_ReleaseWrite(&cm_scacheLock);
5508
5509     return code;
5510 }
5511
5512 cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
5513 {
5514 #ifdef DEBUG
5515     osi_assertx((process_id & 0xffffffff) == process_id, "unexpected process_id");
5516     osi_assertx((session_id & 0xffff) == session_id, "unexpected session_id");
5517     osi_assertx((file_id & 0xffff) == file_id, "unexpected file_id");
5518 #endif
5519
5520     return 
5521         (((cm_key_t) (process_id & 0xffffffff)) << 32) |
5522         (((cm_key_t) (session_id & 0xffff)) << 16) |
5523         (((cm_key_t) (file_id & 0xffff)));
5524 }
5525
5526 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags)
5527 {
5528     if (flags & CM_UNLOCK_BY_FID) {
5529         return ((k1 & 0xffffffff) == (k2 & 0xffffffff));
5530     } else {
5531         return (k1 == k2);
5532     }
5533 }
5534
5535 void cm_ReleaseAllLocks(void)
5536 {
5537     cm_scache_t *scp;
5538     cm_req_t req;
5539     cm_user_t *userp;
5540     cm_key_t   key;
5541     cm_file_lock_t *fileLock;
5542     unsigned int i;
5543
5544     for (i = 0; i < cm_data.scacheHashTableSize; i++)
5545     {
5546         for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5547             while (scp->fileLocksH != NULL) {
5548                 lock_ObtainWrite(&scp->rw);
5549                 lock_ObtainWrite(&cm_scacheLock);
5550                 if (!scp->fileLocksH) {
5551                     lock_ReleaseWrite(&cm_scacheLock);
5552                     lock_ReleaseWrite(&scp->rw);
5553                     break;
5554                 }
5555                 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5556                 userp = fileLock->userp;
5557                 cm_HoldUser(userp);
5558                 key = fileLock->key;
5559                 cm_HoldSCacheNoLock(scp);
5560                 lock_ReleaseWrite(&cm_scacheLock);
5561                 cm_UnlockByKey(scp, key, 0, userp, &req);
5562                 cm_ReleaseSCache(scp);
5563                 cm_ReleaseUser(userp);
5564                 lock_ReleaseWrite(&scp->rw);
5565             }
5566         }
5567     }
5568 }