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