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