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