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