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