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