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