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