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