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