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