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