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