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