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