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