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