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