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