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