windows-misc-20050722
[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     lock_ReleaseMutex(&scp->mx);
267
268     return code;
269 }
270
271 /* return success if we can open this file in this mode */
272 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
273                     unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp)
274 {
275     long rights;
276     long code;
277
278     /* Always allow delete; the RPC will tell us if it's OK */
279     if (desiredAccess == DELETE)
280         return 0;
281
282     rights = 0;
283
284     if (desiredAccess & AFS_ACCESS_READ)
285         rights |= PRSFS_READ;
286
287     if ((desiredAccess & AFS_ACCESS_WRITE)
288          || createDisp == 4)
289         rights |= PRSFS_WRITE;
290
291     lock_ObtainMutex(&scp->mx);
292
293     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
294                       CM_SCACHESYNC_GETSTATUS
295                       | CM_SCACHESYNC_NEEDCALLBACK);
296     lock_ReleaseMutex(&scp->mx);
297
298     /*
299      * If the open will fail because the volume is readonly, then we will
300      * return an access denied error instead.  This is to help brain-dead
301      * apps run correctly on replicated volumes.
302      * See defect 10007 for more information.
303      */
304     if (code == CM_ERROR_READONLY)
305         code = CM_ERROR_NOACCESS;
306
307     return code;
308 }
309
310 /*
311  * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
312  * done in three steps:
313  * (1) open for deletion (NT_CREATE_AND_X)
314  * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
315  * (3) close (CLOSE)
316  * We must not do the RPC until step 3.  But if we are going to return an error
317  * code (e.g. directory not empty), we must return it by step 2, otherwise most
318  * clients will not notice it.  So we do a preliminary check.  For deleting
319  * files, this is almost free, since we have already done the RPC to get the
320  * parent directory's status bits.  But for deleting directories, we must do an
321  * additional RPC to get the directory's data to check if it is empty.  Sigh.
322  */
323 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
324         cm_req_t *reqp)
325 {
326     long code;
327     osi_hyper_t thyper;
328     cm_buf_t *bufferp;
329     cm_dirEntry_t *dep;
330     unsigned short *hashTable;
331     unsigned int i, idx;
332     int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
333
334     /* First check permissions */
335     lock_ObtainMutex(&dscp->mx);
336     code = cm_SyncOp(dscp, NULL, userp, reqp, PRSFS_DELETE,
337                       CM_SCACHESYNC_GETSTATUS
338                       | CM_SCACHESYNC_NEEDCALLBACK);
339     lock_ReleaseMutex(&dscp->mx);
340     if (code)
341         return code;
342
343     /* If deleting directory, must be empty */
344
345     if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
346         return code;
347
348     thyper.HighPart = 0; thyper.LowPart = 0;
349     lock_ObtainRead(&scp->bufCreateLock);
350     code = buf_Get(scp, &thyper, &bufferp);
351     lock_ReleaseRead(&scp->bufCreateLock);
352     if (code)
353         return code;
354
355     lock_ObtainMutex(&bufferp->mx);
356     lock_ObtainMutex(&scp->mx);
357     while (1) {
358         code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
359                           CM_SCACHESYNC_NEEDCALLBACK
360                           | CM_SCACHESYNC_READ
361                           | CM_SCACHESYNC_BUFLOCKED);
362         if (code)
363             break;
364
365         if (cm_HaveBuffer(scp, bufferp, 1))
366             break;
367
368         /* otherwise, load the buffer and try again */
369         lock_ReleaseMutex(&bufferp->mx);
370         code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
371         lock_ReleaseMutex(&scp->mx);
372         lock_ObtainMutex(&bufferp->mx);
373         lock_ObtainMutex(&scp->mx);
374         if (code)
375             break;
376     }
377
378     /* We try to determine emptiness without looking beyond the first page,
379      * and without assuming "." and ".." are present and are on the first
380      * page (though these assumptions might, after all, be reasonable).
381      */
382     hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
383     for (i=0; i<128; i++) {
384         idx = ntohs(hashTable[i]);
385         while (idx) {
386             if (idx >= 64) {
387                 BeyondPage = 1;
388                 break;
389             }
390             dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
391             if (strcmp(dep->name, ".") == 0)
392                 HaveDot = 1;
393             else if (strcmp(dep->name, "..") == 0)
394                 HaveDotDot = 1;
395             else {
396                 code = CM_ERROR_NOTEMPTY;
397                 goto done;
398             }
399             idx = ntohs(dep->next);
400         }
401     }
402     if (BeyondPage && HaveDot && HaveDotDot)
403         code = CM_ERROR_NOTEMPTY;
404     else
405         code = 0;
406   done:   
407     lock_ReleaseMutex(&bufferp->mx);
408     buf_Release(bufferp);
409     lock_ReleaseMutex(&scp->mx);
410     return code;
411 }       
412
413 /*
414  * Iterate through all entries in a directory.
415  * When the function funcp is called, the buffer is locked but the
416  * directory vnode is not.
417  *
418  * If the retscp parameter is not NULL, the parmp must be a 
419  * cm_lookupSearch_t object.  
420  */
421 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
422                   osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
423                   cm_scache_t **retscp)
424 {
425     char *tp;
426     long code;
427     cm_dirEntry_t *dep;
428     cm_buf_t *bufferp;
429     long temp;
430     osi_hyper_t dirLength;
431     osi_hyper_t bufferOffset;
432     osi_hyper_t curOffset;
433     osi_hyper_t thyper;
434     long entryInDir;
435     long entryInBuffer;
436     cm_pageHeader_t *pageHeaderp;
437     int slotInPage;
438     long nextEntryCookie;
439     int numDirChunks;   /* # of 32 byte dir chunks in this entry */
440         
441     /* get the directory size */
442     lock_ObtainMutex(&scp->mx);
443     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
444                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
445     if (code) {
446         lock_ReleaseMutex(&scp->mx);
447         return code;
448     }
449         
450     if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
451         lock_ReleaseMutex(&scp->mx);
452         return CM_ERROR_NOTDIR;
453     }   
454
455     if (retscp)                         /* if this is a lookup call */
456     {
457         cm_lookupSearch_t*      sp = parmp;
458
459 #ifdef AFS_FREELANCE_CLIENT
460         /* Freelance entries never end up in the DNLC because they
461          * do not have an associated cm_server_t
462          */
463     if ( !(cm_freelanceEnabled &&
464             sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
465             sp->fid.volume==AFS_FAKE_ROOT_VOL_ID ) )
466 #endif /* AFS_FREELANCE_CLIENT */
467     {
468         int casefold = sp->caseFold;
469         sp->caseFold = 0; /* we have a strong preference for exact matches */
470         if ( *retscp = cm_dnlcLookup(scp, sp))  /* dnlc hit */
471         {
472             sp->caseFold = casefold;
473             lock_ReleaseMutex(&scp->mx);
474             return 0;
475         }
476         sp->caseFold = casefold;
477     }
478     }   
479
480     /*
481      * XXX We only get the length once.  It might change when we drop the
482      * lock.
483      */
484     dirLength = scp->length;
485
486     lock_ReleaseMutex(&scp->mx);
487
488     bufferp = NULL;
489     bufferOffset.LowPart = bufferOffset.HighPart = 0;
490     if (startOffsetp)
491         curOffset = *startOffsetp;
492     else {
493         curOffset.HighPart = 0;
494         curOffset.LowPart = 0;
495     }   
496
497     while (1) {
498         /* make sure that curOffset.LowPart doesn't point to the first
499          * 32 bytes in the 2nd through last dir page, and that it
500          * doesn't point at the first 13 32-byte chunks in the first
501          * dir page, since those are dir and page headers, and don't
502          * contain useful information.
503          */
504         temp = curOffset.LowPart & (2048-1);
505         if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
506             /* we're in the first page */
507             if (temp < 13*32) temp = 13*32;
508         }
509         else {
510             /* we're in a later dir page */
511             if (temp < 32) temp = 32;
512         }       
513                 
514         /* make sure the low order 5 bits are zero */
515         temp &= ~(32-1);
516                 
517         /* now put temp bits back ito curOffset.LowPart */
518         curOffset.LowPart &= ~(2048-1);
519         curOffset.LowPart |= temp;
520
521         /* check if we've passed the dir's EOF */
522         if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
523             break;
524                 
525         /* see if we can use the bufferp we have now; compute in which
526          * page the current offset would be, and check whether that's
527          * the offset of the buffer we have.  If not, get the buffer.
528          */
529         thyper.HighPart = curOffset.HighPart;
530         thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
531         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
532             /* wrong buffer */
533             if (bufferp) {
534                 lock_ReleaseMutex(&bufferp->mx);
535                 buf_Release(bufferp);
536                 bufferp = NULL;
537             }
538
539             lock_ObtainRead(&scp->bufCreateLock);
540             code = buf_Get(scp, &thyper, &bufferp);
541             lock_ReleaseRead(&scp->bufCreateLock);
542             if (code) {
543                 /* if buf_Get() fails we do not have a buffer object to lock */
544                 bufferp = NULL;
545                 break;
546             }
547
548 #ifdef AFSIFS
549             /* Why was this added for IFS? - jaltman 06/18/2006 */
550             lock_ObtainMutex(&scp->mx);
551             if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
552                  && (scp->bulkStatProgress.QuadPart <= thyper.QuadPart))
553             {
554                 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
555                 cm_TryBulkStat(scp, &thyper, userp, reqp);
556                 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
557                 scp->bulkStatProgress = thyper;
558             }
559             lock_ReleaseMutex(&scp->mx);
560 #endif
561
562             lock_ObtainMutex(&bufferp->mx);
563             bufferOffset = thyper;
564
565             /* now get the data in the cache */
566             while (1) {
567                 lock_ObtainMutex(&scp->mx);
568                 code = cm_SyncOp(scp, bufferp, userp, reqp,
569                                   PRSFS_LOOKUP,
570                                   CM_SCACHESYNC_NEEDCALLBACK
571                                   | CM_SCACHESYNC_READ
572                                   | CM_SCACHESYNC_BUFLOCKED);
573                 if (code) {
574                     lock_ReleaseMutex(&scp->mx);
575                     break;
576                 }
577                                 
578                 if (cm_HaveBuffer(scp, bufferp, 1)) {
579                     lock_ReleaseMutex(&scp->mx);
580                     break;
581                 }
582
583                 /* otherwise, load the buffer and try again */
584                 lock_ReleaseMutex(&bufferp->mx);
585                 code = cm_GetBuffer(scp, bufferp, NULL, userp,
586                                     reqp);
587                 lock_ReleaseMutex(&scp->mx);
588                 lock_ObtainMutex(&bufferp->mx);
589                 if (code) 
590                     break;
591             }
592             if (code) {
593                 lock_ReleaseMutex(&bufferp->mx);
594                 buf_Release(bufferp);
595                 bufferp = NULL;
596                 break;
597             }
598         }       /* if (wrong buffer) ... */
599            
600         /* now we have the buffer containing the entry we're interested
601          * in; copy it out if it represents a non-deleted entry.
602          */
603         entryInDir = curOffset.LowPart & (2048-1);
604         entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
605
606         /* page header will help tell us which entries are free.  Page
607          * header can change more often than once per buffer, since
608          * AFS 3 dir page size may be less than (but not more than) a
609          * buffer package buffer.
610          */
611         /* only look intra-buffer */
612         temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
613         temp &= ~(2048 - 1);    /* turn off intra-page bits */
614         pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
615
616         /* now determine which entry we're looking at in the page.  If
617          * it is free (there's a free bitmap at the start of the dir),
618          * we should skip these 32 bytes.
619          */
620         slotInPage = (entryInDir & 0x7e0) >> 5;
621         if (!(pageHeaderp->freeBitmap[slotInPage>>3]
622                & (1 << (slotInPage & 0x7)))) {
623             /* this entry is free */
624             numDirChunks = 1;   /* only skip this guy */
625             goto nextEntry;
626         }
627
628         tp = bufferp->datap + entryInBuffer;
629         dep = (cm_dirEntry_t *) tp;     /* now points to AFS3 dir entry */
630
631         /* while we're here, compute the next entry's location, too,
632          * since we'll need it when writing out the cookie into the
633          * dir listing stream.
634          */
635         numDirChunks = cm_NameEntries(dep->name, NULL);
636                 
637         /* compute the offset of the cookie representing the next entry */
638         nextEntryCookie = curOffset.LowPart
639             + (CM_DIR_CHUNKSIZE * numDirChunks);
640
641         if (dep->fid.vnode != 0) {
642             /* this is one of the entries to use: it is not deleted */
643             code = (*funcp)(scp, dep, parmp, &curOffset);
644             if (code) 
645                 break;
646         }       /* if we're including this name */
647                 
648       nextEntry:
649         /* and adjust curOffset to be where the new cookie is */
650         thyper.HighPart = 0;
651         thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
652         curOffset = LargeIntegerAdd(thyper, curOffset);
653     }           /* while copying data for dir listing */
654
655     /* release the mutex */
656     if (bufferp) {
657         lock_ReleaseMutex(&bufferp->mx);
658         buf_Release(bufferp);
659     }
660     return code;
661 }
662
663 int cm_NoneUpper(char *s)
664 {
665     char c;
666     while (c = *s++)
667         if (c >= 'A' && c <= 'Z')
668             return 0;
669     return 1;
670 }
671
672 int cm_NoneLower(char *s)
673 {
674     char c;
675     while (c = *s++)
676         if (c >= 'a' && c <= 'z')
677             return 0;
678     return 1;
679 }
680
681 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
682                           osi_hyper_t *offp)
683 {
684     cm_lookupSearch_t *sp;
685     int match;
686     char shortName[13];
687     char *matchName;
688
689     sp = (cm_lookupSearch_t *) rockp;
690
691     matchName = dep->name;
692     if (sp->caseFold)
693         match = cm_stricmp(matchName, sp->searchNamep);
694     else
695         match = strcmp(matchName, sp->searchNamep);
696
697     if (match != 0
698          && sp->hasTilde
699          && !cm_Is8Dot3(dep->name)) {
700         matchName = shortName;
701         cm_Gen8Dot3Name(dep, shortName, NULL);
702         if (sp->caseFold)
703             match = cm_stricmp(matchName, sp->searchNamep);
704         else
705             match = strcmp(matchName, sp->searchNamep);
706     }       
707
708     if (match != 0)
709         return 0;
710
711     sp->found = 1;
712     if(!sp->caseFold) 
713         sp->ExactFound = 1;
714
715     if (!sp->caseFold || matchName == shortName) {
716         sp->fid.vnode = ntohl(dep->fid.vnode);
717         sp->fid.unique = ntohl(dep->fid.unique);
718         return CM_ERROR_STOPNOW;
719     }
720
721     /*
722      * If we get here, we are doing a case-insensitive search, and we
723      * have found a match.  Now we determine what kind of match it is:
724      * exact, lower-case, upper-case, or none of the above.  This is done
725      * in order to choose among matches, if there are more than one.
726      */
727
728     /* Exact matches are the best. */
729     match = strcmp(matchName, sp->searchNamep);
730     if (match == 0) {
731         sp->ExactFound = 1;
732         sp->fid.vnode = ntohl(dep->fid.vnode);
733         sp->fid.unique = ntohl(dep->fid.unique);
734         return CM_ERROR_STOPNOW;
735     }
736
737     /* Lower-case matches are next. */
738     if (sp->LCfound)
739         return 0;
740     if (cm_NoneUpper(matchName)) {
741         sp->LCfound = 1;
742         goto inexact;
743     }
744
745     /* Upper-case matches are next. */
746     if (sp->UCfound)
747         return 0;
748     if (cm_NoneLower(matchName)) {
749         sp->UCfound = 1;
750         goto inexact;
751     }
752
753     /* General matches are last. */
754     if (sp->NCfound)
755         return 0;
756     sp->NCfound = 1;
757
758   inexact:
759     sp->fid.vnode = ntohl(dep->fid.vnode);
760     sp->fid.unique = ntohl(dep->fid.unique);
761     return 0;
762 }       
763
764 /* read the contents of a mount point into the appropriate string.
765  * called with locked scp, and returns with locked scp.
766  */
767 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
768 {
769     long code;
770     cm_buf_t *bufp;
771     osi_hyper_t thyper;
772     int tlen;
773
774     if (scp->mountPointStringp[0]) 
775         return 0;
776         
777     /* otherwise, we have to read it in */
778     lock_ReleaseMutex(&scp->mx);
779
780     lock_ObtainRead(&scp->bufCreateLock);
781     thyper.LowPart = thyper.HighPart = 0;
782     code = buf_Get(scp, &thyper, &bufp);
783     lock_ReleaseRead(&scp->bufCreateLock);
784
785     lock_ObtainMutex(&scp->mx);
786     if (code) {
787         return code;
788     }
789     while (1) {
790         code = cm_SyncOp(scp, bufp, userp, reqp, 0,
791                           CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
792         if (code) {
793             goto done;
794         }
795
796         if (cm_HaveBuffer(scp, bufp, 0)) 
797             break;
798
799         /* otherwise load buffer */
800         code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
801         if (code) {
802             goto done;
803         }
804     }
805     /* locked, has callback, has valid data in buffer */
806     if ((tlen = scp->length.LowPart) > 1000) 
807         return CM_ERROR_TOOBIG;
808     if (tlen <= 0) {
809         code = CM_ERROR_INVAL;
810         goto done;
811     }
812
813     /* someone else did the work while we were out */
814     if (scp->mountPointStringp[0]) {
815         code = 0;
816         goto done;
817     }
818
819     /* otherwise, copy out the link */
820     memcpy(scp->mountPointStringp, bufp->datap, tlen);
821
822     /* now make it null-terminated.  Note that the original contents of a
823      * link that is a mount point is "#volname." where "." is there just to
824      * be turned into a null.  That is, we can trash the last char of the
825      * link without damaging the vol name.  This is a stupid convention,
826      * but that's the protocol.
827      */
828     scp->mountPointStringp[tlen-1] = 0;
829     code = 0;
830
831   done:
832     if (bufp) 
833         buf_Release(bufp);
834     return code;
835 }
836
837 /* called with a locked scp and chases the mount point, yielding outScpp.
838  * scp remains locked, just for simplicity of describing the interface.
839  */
840 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
841                          cm_req_t *reqp, cm_scache_t **outScpp)
842 {
843     char *cellNamep;
844     char *volNamep;
845     int tlen;
846     long code;
847     char *cp;
848     char *mpNamep;
849     cm_volume_t *volp;
850     cm_cell_t *cellp;
851     char mtType;
852     cm_fid_t tfid;
853     size_t vnLength;
854     int type;
855
856     if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
857         tfid = scp->mountRootFid;
858         lock_ReleaseMutex(&scp->mx);
859         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
860         lock_ObtainMutex(&scp->mx);
861         return code;
862     }
863
864     /* parse the volume name */
865     mpNamep = scp->mountPointStringp;
866     osi_assert(mpNamep[0]);
867     tlen = strlen(scp->mountPointStringp);
868     mtType = *scp->mountPointStringp;
869     cellNamep = malloc(tlen);
870     volNamep = malloc(tlen);
871
872     cp = strrchr(mpNamep, ':');
873     if (cp) {
874         /* cellular mount point */
875         memset(cellNamep, 0, tlen);
876         strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
877         strcpy(volNamep, cp+1);
878         /* now look up the cell */
879         cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
880     }
881     else {
882         /* normal mt pt */
883         strcpy(volNamep, mpNamep+1);
884
885         cellp = cm_FindCellByID(scp->fid.cell);
886     }
887
888     if (!cellp) {
889         code = CM_ERROR_NOSUCHCELL;
890         goto done;
891     }
892
893     vnLength = strlen(volNamep);
894     if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
895         type = BACKVOL;
896     else if (vnLength >= 10
897               && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
898         type = ROVOL;
899     else
900         type = RWVOL;
901
902     /* check for backups within backups */
903     if (type == BACKVOL
904          && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
905          == CM_SCACHEFLAG_RO) {
906         code = CM_ERROR_NOSUCHVOLUME;
907         goto done;
908     }
909
910     /* now we need to get the volume */
911     lock_ReleaseMutex(&scp->mx);
912     code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
913     lock_ObtainMutex(&scp->mx);
914         
915     if (code == 0) {
916         /* save the parent of the volume root for this is the 
917          * place where the volume is mounted and we must remember 
918          * this in the volume structure rather than just in the 
919          * scache entry lest the scache entry gets recycled 
920          * (defect 11489)
921          */
922         lock_ObtainMutex(&volp->mx);
923         volp->dotdotFid = dscp->fid;
924         lock_ReleaseMutex(&volp->mx);
925
926         scp->mountRootFid.cell = cellp->cellID;
927         /* if the mt pt is in a read-only volume (not just a
928          * backup), and if there is a read-only volume for the
929          * target, and if this is a type '#' mount point, use
930          * the read-only, otherwise use the one specified.
931          */
932         if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
933              && volp->roID != 0 && type == RWVOL)
934             type = ROVOL;
935         if (type == ROVOL)
936             scp->mountRootFid.volume = volp->roID;
937         else if (type == BACKVOL)
938             scp->mountRootFid.volume = volp->bkID;
939         else
940             scp->mountRootFid.volume = volp->rwID;
941
942         /* the rest of the fid is a magic number */
943         scp->mountRootFid.vnode = 1;
944         scp->mountRootFid.unique = 1;
945         scp->mountRootGen = cm_data.mountRootGen;
946
947         tfid = scp->mountRootFid;
948         lock_ReleaseMutex(&scp->mx);
949         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
950         lock_ObtainMutex(&scp->mx);
951     }
952
953   done:
954     free(cellNamep);
955     free(volNamep);
956     return code;
957 }       
958
959 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
960                        cm_req_t *reqp, cm_scache_t **outpScpp)
961 {
962     long code;
963     int dnlcHit = 1;    /* did we hit in the dnlc? yes, we did */
964     cm_scache_t *tscp = NULL;
965     cm_scache_t *mountedScp;
966     cm_lookupSearch_t rock;
967     int getroot;
968
969     if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
970          && strcmp(namep, "..") == 0) {
971         if (dscp->dotdotFid.volume == 0)
972             return CM_ERROR_NOSUCHVOLUME;
973         rock.fid = dscp->dotdotFid;
974         goto haveFid;
975     }
976
977     memset(&rock, 0, sizeof(rock));
978     rock.fid.cell = dscp->fid.cell;
979     rock.fid.volume = dscp->fid.volume;
980     rock.searchNamep = namep;
981     rock.caseFold = (flags & CM_FLAG_CASEFOLD);
982     rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
983
984     /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
985     code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
986                         (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
987
988     /* code == 0 means we fell off the end of the dir, while stopnow means
989      * that we stopped early, probably because we found the entry we're
990      * looking for.  Any other non-zero code is an error.
991      */
992     if (code && code != CM_ERROR_STOPNOW) {
993         /* if the cm_scache_t we are searching in is not a directory 
994          * we must return path not found because the error 
995          * is to describe the final component not an intermediary
996          */
997         if (code == CM_ERROR_NOTDIR) {
998             if (flags & CM_FLAG_CHECKPATH)
999                 return CM_ERROR_NOSUCHPATH;
1000             else
1001                 return CM_ERROR_NOSUCHFILE;
1002         }
1003         return code;
1004     }
1005
1006     getroot = (dscp==cm_data.rootSCachep) ;
1007     if (!rock.found) {
1008         if (!cm_freelanceEnabled || !getroot) {
1009             if (flags & CM_FLAG_CHECKPATH)
1010                 return CM_ERROR_NOSUCHPATH;
1011             else
1012                 return CM_ERROR_NOSUCHFILE;
1013         }
1014         else {  /* nonexistent dir on freelance root, so add it */
1015             char fullname[200] = ".";
1016             int  found = 0;
1017
1018             osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s", 
1019                       osi_LogSaveString(afsd_logp,namep));
1020             if (namep[0] == '.') {
1021                 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1022                     found = 1;
1023                     if ( stricmp(&namep[1], &fullname[1]) )
1024                         code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1025                     else
1026                         code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1027                 }
1028             } else {
1029                 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1030                     found = 1;
1031                     if ( stricmp(namep, fullname) )
1032                         code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1033                     else
1034                         code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1035                 }
1036             }
1037             if (!found || code < 0) {   /* add mount point failed, so give up */
1038                 if (flags & CM_FLAG_CHECKPATH)
1039                     return CM_ERROR_NOSUCHPATH;
1040                 else
1041                     return CM_ERROR_NOSUCHFILE;
1042             }
1043             tscp = NULL;   /* to force call of cm_GetSCache */
1044         }
1045     }
1046
1047   haveFid:       
1048     if ( !tscp )    /* we did not find it in the dnlc */
1049     {
1050         dnlcHit = 0;    
1051         code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1052         if (code) 
1053             return code;
1054     }       
1055     /* tscp is now held */
1056
1057     lock_ObtainMutex(&tscp->mx);
1058     code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1059                       CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1060     if (code) { 
1061         lock_ReleaseMutex(&tscp->mx);
1062         cm_ReleaseSCache(tscp);
1063         return code;
1064     }
1065     /* tscp is now locked */
1066
1067     if (!(flags & CM_FLAG_NOMOUNTCHASE)
1068          && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1069         /* mount points are funny: they have a volume name to mount
1070          * the root of.
1071          */
1072         code = cm_ReadMountPoint(tscp, userp, reqp);
1073         if (code == 0)
1074             code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1075                                         &mountedScp);
1076         lock_ReleaseMutex(&tscp->mx);
1077         cm_ReleaseSCache(tscp);
1078         if (code) {
1079             return code;
1080         }
1081         tscp = mountedScp;
1082     }
1083     else {
1084         lock_ReleaseMutex(&tscp->mx);
1085     }
1086
1087     /* copy back pointer */
1088     *outpScpp = tscp;
1089
1090     /* insert scache in dnlc */
1091     if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1092         /* lock the directory entry to prevent racing callback revokes */
1093         lock_ObtainMutex(&dscp->mx);
1094         if ( dscp->cbServerp && dscp->cbExpires )
1095             cm_dnlcEnter(dscp, namep, tscp);
1096         lock_ReleaseMutex(&dscp->mx);
1097     }
1098
1099     /* and return */
1100     return 0;
1101 }
1102
1103 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1104 {
1105     char *tp;
1106     int prefixCount;
1107
1108     tp = strrchr(inp, '@');
1109     if (tp == NULL) 
1110         return 0;               /* no @sys */
1111
1112     if (strcmp(tp, "@sys") != 0) 
1113         return 0;       /* no @sys */
1114
1115     /* caller just wants to know if this is a valid @sys type of name */
1116     if (outp == NULL) 
1117         return 1;
1118
1119     if (index >= MAXNUMSYSNAMES)
1120         return -1;
1121
1122     /* otherwise generate the properly expanded @sys name */
1123     prefixCount = tp - inp;
1124
1125     strncpy(outp, inp, prefixCount);    /* copy out "a." from "a.@sys" */
1126     outp[prefixCount] = 0;              /* null terminate the "a." */
1127     strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1128     return 1;
1129 }   
1130
1131 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1132                cm_req_t *reqp, cm_scache_t **outpScpp)
1133 {
1134     long code;
1135     char tname[256];
1136     int sysNameIndex = 0;
1137     cm_scache_t *scp = 0;
1138
1139     if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1140         if (flags & CM_FLAG_CHECKPATH)
1141             return CM_ERROR_NOSUCHPATH;
1142         else
1143             return CM_ERROR_NOSUCHFILE;
1144     }
1145
1146     for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1147         code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1148         if (code > 0) {
1149             code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1150             if (code == 0) {
1151                 *outpScpp = scp;
1152                 return 0;
1153             }
1154             if (scp) {
1155                 cm_ReleaseSCache(scp);
1156                 scp = 0;
1157             }
1158         } else {
1159             return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
1160         }
1161     }
1162
1163     /* None of the possible sysName expansions could be found */
1164     if (flags & CM_FLAG_CHECKPATH)
1165         return CM_ERROR_NOSUCHPATH;
1166     else
1167         return CM_ERROR_NOSUCHFILE;
1168 }
1169
1170 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1171 {
1172     long code;
1173     cm_conn_t *connp;
1174     AFSFid afsFid;
1175     int sflags;
1176     AFSFetchStatus newDirStatus;
1177     AFSVolSync volSync;
1178     struct rx_connection * callp;
1179
1180 #ifdef AFS_FREELANCE_CLIENT
1181     if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1182         /* deleting a mount point from the root dir. */
1183         code = cm_FreelanceRemoveMount(namep);
1184         return code;
1185     }
1186 #endif  
1187
1188     /* make sure we don't screw up the dir status during the merge */
1189     lock_ObtainMutex(&dscp->mx);
1190     sflags = CM_SCACHESYNC_STOREDATA;
1191     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1192     lock_ReleaseMutex(&dscp->mx);
1193     if (code) 
1194         return code;
1195
1196     /* make the RPC */
1197     afsFid.Volume = dscp->fid.volume;
1198     afsFid.Vnode = dscp->fid.vnode;
1199     afsFid.Unique = dscp->fid.unique;
1200
1201     osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%x", (long) dscp);
1202     do {
1203         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1204         if (code) 
1205             continue;
1206
1207         callp = cm_GetRxConn(connp);
1208         code = RXAFS_RemoveFile(callp, &afsFid, namep,
1209                                  &newDirStatus, &volSync);
1210         rx_PutConnection(callp);
1211
1212     } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1213     code = cm_MapRPCError(code, reqp);
1214
1215     if (code)
1216         osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1217     else
1218         osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1219
1220     lock_ObtainMutex(&dscp->mx);
1221     cm_dnlcRemove(dscp, namep);
1222     cm_SyncOpDone(dscp, NULL, sflags);
1223     if (code == 0) 
1224         cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1225     lock_ReleaseMutex(&dscp->mx);
1226
1227     return code;
1228 }
1229
1230 /* called with a locked vnode, and fills in the link info.
1231  * returns this the vnode still locked.
1232  */
1233 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1234 {
1235     long code;
1236     cm_buf_t *bufp;
1237     long temp;
1238     osi_hyper_t thyper;
1239
1240     lock_AssertMutex(&linkScp->mx);
1241     if (!linkScp->mountPointStringp[0]) {
1242         /* read the link data */
1243         lock_ReleaseMutex(&linkScp->mx);
1244         thyper.LowPart = thyper.HighPart = 0;
1245         code = buf_Get(linkScp, &thyper, &bufp);
1246         lock_ObtainMutex(&linkScp->mx);
1247         if (code) 
1248             return code;
1249         while (1) {
1250             code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1251                               CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1252             if (code) {
1253                 buf_Release(bufp);
1254                 return code;
1255             }
1256             if (cm_HaveBuffer(linkScp, bufp, 0)) 
1257                 break;
1258
1259             code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1260             if (code) {
1261                 buf_Release(bufp);
1262                 return code;
1263             }
1264         } /* while loop to get the data */
1265                 
1266         /* now if we still have no link read in,
1267          * copy the data from the buffer */
1268         if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1269             buf_Release(bufp);
1270             return CM_ERROR_TOOBIG;
1271         }
1272
1273         /* otherwise, it fits; make sure it is still null (could have
1274          * lost race with someone else referencing this link above),
1275          * and if so, copy in the data.
1276          */
1277         if (!linkScp->mountPointStringp[0]) {
1278             strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1279             linkScp->mountPointStringp[temp] = 0;       /* null terminate */
1280         }
1281         buf_Release(bufp);
1282     }   /* don't have sym link contents cached */
1283
1284     return 0;
1285 }       
1286
1287 /* called with a held vnode and a path suffix, with the held vnode being a
1288  * symbolic link.  Our goal is to generate a new path to interpret, and return
1289  * this new path in newSpaceBufferp.  If the new vnode is relative to a dir
1290  * other than the directory containing the symbolic link, then the new root is
1291  * returned in *newRootScpp, otherwise a null is returned there.
1292  */
1293 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1294                       cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1295                       cm_user_t *userp, cm_req_t *reqp)
1296 {
1297     long code = 0;
1298     long len;
1299     char *linkp;
1300     cm_space_t *tsp;
1301
1302     lock_ObtainMutex(&linkScp->mx);
1303     code = cm_HandleLink(linkScp, userp, reqp);
1304     if (code) 
1305         goto done;
1306
1307     /* if we may overflow the buffer, bail out; buffer is signficantly
1308      * bigger than max path length, so we don't really have to worry about
1309      * being a little conservative here.
1310      */
1311     if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1312          >= CM_UTILS_SPACESIZE)
1313         return CM_ERROR_TOOBIG;
1314
1315     tsp = cm_GetSpace();
1316     linkp = linkScp->mountPointStringp;
1317     if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1318         if (strlen(linkp) > cm_mountRootLen)
1319             strcpy(tsp->data, linkp+cm_mountRootLen+1);
1320         else
1321             tsp->data[0] = 0;
1322         *newRootScpp = cm_data.rootSCachep;
1323         cm_HoldSCache(cm_data.rootSCachep);
1324     } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1325         if (!strnicmp(&linkp[2], cm_NetbiosName, (len = strlen(cm_NetbiosName)))) 
1326         {
1327             char * p = &linkp[len + 3];
1328             if (strnicmp(p, "all", 3) == 0)
1329                 p += 4;
1330
1331             strcpy(tsp->data, p);
1332             for (p = tsp->data; *p; p++) {
1333                 if (*p == '\\')
1334                     *p = '/';
1335             }
1336             *newRootScpp = cm_data.rootSCachep;
1337             cm_HoldSCache(cm_data.rootSCachep);
1338         } else {
1339             linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1340             strcpy(tsp->data, linkp);
1341             *newRootScpp = NULL;
1342             code = CM_ERROR_PATH_NOT_COVERED;
1343         }
1344     } else if ( !strnicmp(linkp, "msdfs:", (len = strlen("msdfs:"))) ) {
1345         linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1346         strcpy(tsp->data, linkp);
1347         *newRootScpp = NULL;
1348         code = CM_ERROR_PATH_NOT_COVERED;
1349     } else if (*linkp == '\\' || *linkp == '/') {
1350 #if 0   
1351         /* formerly, this was considered to be from the AFS root,
1352          * but this seems to create problems.  instead, we will just
1353          * reject the link */
1354         strcpy(tsp->data, linkp+1);
1355         *newRootScpp = cm_data.rootSCachep;
1356         cm_HoldSCache(cm_data.rootSCachep);
1357 #else
1358         /* we still copy the link data into the response so that 
1359          * the user can see what the link points to
1360          */
1361         linkScp->fileType = CM_SCACHETYPE_INVALID;
1362         strcpy(tsp->data, linkp);
1363         *newRootScpp = NULL;
1364         code = CM_ERROR_NOSUCHPATH;
1365 #endif  
1366     } else {
1367         /* a relative link */
1368         strcpy(tsp->data, linkp);
1369         *newRootScpp = NULL;
1370     }
1371     if (pathSuffixp[0] != 0) {  /* if suffix string is non-null */
1372         strcat(tsp->data, "\\");
1373         strcat(tsp->data, pathSuffixp);
1374     }
1375     *newSpaceBufferp = tsp;
1376
1377   done:
1378     lock_ReleaseMutex(&linkScp->mx);
1379     return code;
1380 }
1381
1382 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1383                cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1384 {
1385     long code;
1386     char *tp;                   /* ptr moving through input buffer */
1387     char tc;                    /* temp char */
1388     int haveComponent;          /* has new component started? */
1389     char component[256];        /* this is the new component */
1390     char *cp;                   /* component name being assembled */
1391     cm_scache_t *tscp;          /* current location in the hierarchy */
1392     cm_scache_t *nscp;          /* next dude down */
1393     cm_scache_t *dirScp;        /* last dir we searched */
1394     cm_scache_t *linkScp;       /* new root for the symlink we just
1395     * looked up */
1396     cm_space_t *psp;            /* space for current path, if we've hit
1397     * any symlinks */
1398     cm_space_t *tempsp;         /* temp vbl */
1399     char *restp;                /* rest of the pathname to interpret */
1400     int symlinkCount;           /* count of # of symlinks traversed */
1401     int extraFlag;              /* avoid chasing mt pts for dir cmd */
1402     int phase = 1;              /* 1 = tidPathp, 2 = pathp */
1403
1404     tp = tidPathp;
1405     if (tp == NULL) {
1406         tp = pathp;
1407         phase = 2;
1408     }
1409     if (tp == NULL) {
1410         tp = "";
1411     }
1412     haveComponent = 0;
1413     psp = NULL;
1414     tscp = rootSCachep;
1415     cm_HoldSCache(tscp);
1416     symlinkCount = 0;
1417     dirScp = 0;
1418
1419     while (1) {
1420         tc = *tp++;
1421
1422         /* map Unix slashes into DOS ones so we can interpret Unix
1423          * symlinks properly
1424          */
1425         if (tc == '/') 
1426             tc = '\\';
1427
1428         if (!haveComponent) {
1429             if (tc == '\\') {
1430                 continue;
1431             } else if (tc == 0) {
1432                 if (phase == 1) {
1433                     phase = 2;
1434                     tp = pathp;
1435                     continue;
1436                 }
1437                 code = 0;
1438                 break;
1439             } else {
1440                 haveComponent = 1;
1441                 cp = component;
1442                 *cp++ = tc;
1443             }
1444         } else {
1445             /* we have a component here */
1446             if (tc == 0 || tc == '\\') {
1447                 /* end of the component; we're at the last
1448                  * component if tc == 0.  However, if the last
1449                  * is a symlink, we have more to do.
1450                  */
1451                 *cp++ = 0;      /* add null termination */
1452                 extraFlag = 0;
1453                 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1454                     extraFlag = CM_FLAG_NOMOUNTCHASE;
1455                 code = cm_Lookup(tscp, component,
1456                                   flags | extraFlag,
1457                                   userp, reqp, &nscp);
1458                 if (code) {
1459                     cm_ReleaseSCache(tscp);
1460                     if (dirScp)
1461                         cm_ReleaseSCache(dirScp);
1462                     if (psp) 
1463                         cm_FreeSpace(psp);
1464                     if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK)
1465                         return CM_ERROR_NOSUCHPATH;
1466                     else
1467                         return code;
1468                 }
1469                 haveComponent = 0;      /* component done */
1470                 if (dirScp)
1471                     cm_ReleaseSCache(dirScp);
1472                 dirScp = tscp;          /* for some symlinks */
1473                 tscp = nscp;            /* already held */
1474                 nscp = 0;
1475                 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1476                     code = 0;
1477                     if (dirScp) {
1478                         cm_ReleaseSCache(dirScp);
1479                         dirScp = 0;
1480                     }
1481                     break;
1482                 }
1483
1484                 /* now, if tscp is a symlink, we should follow
1485                  * it and assemble the path again.
1486                  */
1487                 lock_ObtainMutex(&tscp->mx);
1488                 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1489                                   CM_SCACHESYNC_GETSTATUS
1490                                   | CM_SCACHESYNC_NEEDCALLBACK);
1491                 if (code) {
1492                     lock_ReleaseMutex(&tscp->mx);
1493                     cm_ReleaseSCache(tscp);
1494                     tscp = 0;
1495                     if (dirScp) {
1496                         cm_ReleaseSCache(dirScp);
1497                         dirScp = 0;
1498                     }
1499                     break;
1500                 }
1501                 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1502                     /* this is a symlink; assemble a new buffer */
1503                     lock_ReleaseMutex(&tscp->mx);
1504                     if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1505                         cm_ReleaseSCache(tscp);
1506                         tscp = 0;
1507                         if (dirScp) {
1508                             cm_ReleaseSCache(dirScp);
1509                             dirScp = 0;
1510                         }
1511                         if (psp) 
1512                             cm_FreeSpace(psp);
1513                         return CM_ERROR_TOO_MANY_SYMLINKS;
1514                     }
1515                     if (tc == 0) 
1516                         restp = "";
1517                     else 
1518                         restp = tp;
1519                     code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1520                     if (code) {
1521                         /* something went wrong */
1522                         cm_ReleaseSCache(tscp);
1523                         tscp = 0;
1524                         if (dirScp) {
1525                             cm_ReleaseSCache(dirScp);
1526                             dirScp = 0;
1527                         }
1528                         break;
1529                     }
1530
1531                     /* otherwise, tempsp has the new path,
1532                      * and linkScp is the new root from
1533                      * which to interpret that path.
1534                      * Continue with the namei processing,
1535                      * also doing the bookkeeping for the
1536                      * space allocation and tracking the
1537                      * vnode reference counts.
1538                      */
1539                     if (psp) 
1540                         cm_FreeSpace(psp);
1541                     psp = tempsp;
1542                     tp = psp->data;
1543                     cm_ReleaseSCache(tscp);
1544                     tscp = linkScp;
1545                     linkScp = 0;
1546                     /* already held
1547                      * by AssembleLink
1548                      * now, if linkScp is null, that's
1549                      * AssembleLink's way of telling us that
1550                      * the sym link is relative to the dir
1551                      * containing the link.  We have a ref
1552                      * to it in dirScp, and we hold it now
1553                      * and reuse it as the new spot in the
1554                      * dir hierarchy.
1555                      */
1556                     if (tscp == NULL) {
1557                         tscp = dirScp;
1558                         dirScp = 0;
1559                     }
1560                 } else {
1561                     /* not a symlink, we may be done */
1562                     lock_ReleaseMutex(&tscp->mx);
1563                     if (tc == 0) {
1564                         if (phase == 1) {
1565                             phase = 2;
1566                             tp = pathp;
1567                             continue;
1568                         }
1569                         if (dirScp) {
1570                             cm_ReleaseSCache(dirScp);
1571                             dirScp = 0;
1572                         }
1573                         code = 0;
1574                         break;
1575                     }
1576                 }
1577                 if (dirScp) {
1578                     cm_ReleaseSCache(dirScp);
1579                     dirScp = 0;
1580                 }
1581             } /* end of a component */
1582             else 
1583                 *cp++ = tc;
1584         } /* we have a component */
1585     } /* big while loop over all components */
1586
1587     /* already held */
1588     if (dirScp)
1589         cm_ReleaseSCache(dirScp);
1590     if (psp) 
1591         cm_FreeSpace(psp);
1592     if (code == 0) 
1593         *outScpp = tscp;
1594     else if (tscp)
1595         cm_ReleaseSCache(tscp);
1596     return code;
1597 }
1598
1599 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1600  * We chase the link, and return a held pointer to the target, if it exists,
1601  * in *outScpp.  If we succeed, we return 0, otherwise we return an error code
1602  * and do not hold or return a target vnode.
1603  *
1604  * This is very similar to calling cm_NameI with the last component of a name,
1605  * which happens to be a symlink, except that we've already passed by the name.
1606  *
1607  * This function is typically called by the directory listing functions, which
1608  * encounter symlinks but need to return the proper file length so programs
1609  * like "more" work properly when they make use of the attributes retrieved from
1610  * the dir listing.
1611  *
1612  * The input vnode should not be locked when this function is called.
1613  */
1614 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1615                          cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1616 {
1617     long code;
1618     cm_space_t *spacep;
1619     cm_scache_t *newRootScp;
1620
1621     osi_Log1(afsd_logp, "Evaluating symlink scp 0x%x", linkScp);
1622
1623     code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1624     if (code) 
1625         return code;
1626
1627     /* now, if newRootScp is NULL, we're really being told that the symlink
1628      * is relative to the current directory (dscp).
1629      */
1630     if (newRootScp == NULL) {
1631         newRootScp = dscp;
1632         cm_HoldSCache(dscp);
1633     }
1634
1635     code = cm_NameI(newRootScp, spacep->data,
1636                      CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1637                      userp, NULL, reqp, outScpp);
1638
1639     if (code == CM_ERROR_NOSUCHFILE)
1640         code = CM_ERROR_NOSUCHPATH;
1641
1642     /* this stuff is allocated no matter what happened on the namei call,
1643      * so free it */
1644     cm_FreeSpace(spacep);
1645     cm_ReleaseSCache(newRootScp);
1646
1647     return code;
1648 }
1649
1650 /* make this big enough so that one buffer of dir pages won't overflow.  We'll
1651  * check anyway, but we want to minimize the chance that we have to leave stuff
1652  * unstat'd.
1653  */
1654 #define CM_BULKMAX              128
1655
1656 /* rock for bulk stat calls */
1657 typedef struct cm_bulkStat {
1658     osi_hyper_t bufOffset;      /* only do it for things in this buffer page */
1659
1660     /* info for the actual call */
1661     int counter;                        /* next free slot */
1662     AFSFid fids[CM_BULKMAX];
1663     AFSFetchStatus stats[CM_BULKMAX];
1664     AFSCallBack callbacks[CM_BULKMAX];
1665 } cm_bulkStat_t;
1666
1667 /* for a given entry, make sure that it isn't in the stat cache, and then
1668  * add it to the list of file IDs to be obtained.
1669  *
1670  * Don't bother adding it if we already have a vnode.  Note that the dir
1671  * is locked, so we have to be careful checking the vnode we're thinking of
1672  * processing, to avoid deadlocks.
1673  */
1674 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1675                      osi_hyper_t *offp)
1676 {
1677     osi_hyper_t thyper;
1678     cm_bulkStat_t *bsp;
1679     int i;
1680     cm_scache_t *tscp;
1681     cm_fid_t tfid;
1682
1683     bsp = rockp;
1684
1685     /* Don't overflow bsp. */
1686     if (bsp->counter >= CM_BULKMAX)
1687         return CM_ERROR_STOPNOW;
1688
1689     thyper.LowPart = cm_data.buf_blockSize;
1690     thyper.HighPart = 0;
1691     thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1692
1693     /* thyper is now the first byte past the end of the record we're
1694      * interested in, and bsp->bufOffset is the first byte of the record
1695      * we're interested in.
1696      * Skip data in the others.
1697      * Skip '.' and '..'
1698      */
1699     if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1700         return 0;
1701     if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1702         return CM_ERROR_STOPNOW;
1703     if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1704         return 0;
1705
1706     tfid.cell = scp->fid.cell;
1707     tfid.volume = scp->fid.volume;
1708     tfid.vnode = ntohl(dep->fid.vnode);
1709     tfid.unique = ntohl(dep->fid.unique);
1710     tscp = cm_FindSCache(&tfid);
1711     if (tscp) {
1712         if (lock_TryMutex(&tscp->mx)) {
1713             /* we have an entry that we can look at */
1714             if (cm_HaveCallback(tscp)) {
1715                 /* we have a callback on it.  Don't bother
1716                  * fetching this stat entry, since we're happy
1717                  * with the info we have.
1718                  */
1719                 lock_ReleaseMutex(&tscp->mx);
1720                 cm_ReleaseSCache(tscp);
1721                 return 0;
1722             }
1723             lock_ReleaseMutex(&tscp->mx);
1724         }       /* got lock */
1725         cm_ReleaseSCache(tscp);
1726     }   /* found entry */
1727
1728 #ifdef AFS_FREELANCE_CLIENT
1729     // yj: if this is a mountpoint under root.afs then we don't want it
1730     // to be bulkstat-ed, instead, we call getSCache directly and under
1731     // getSCache, it is handled specially.
1732     if  ( cm_freelanceEnabled &&
1733           tfid.cell==AFS_FAKE_ROOT_CELL_ID && 
1734           tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1735           !(tfid.vnode==0x1 && tfid.unique==0x1) )
1736     {       
1737         osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1738         return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1739     }
1740 #endif /* AFS_FREELANCE_CLIENT */
1741
1742     i = bsp->counter++;
1743     bsp->fids[i].Volume = scp->fid.volume;
1744     bsp->fids[i].Vnode = tfid.vnode;
1745     bsp->fids[i].Unique = tfid.unique;
1746     return 0;
1747 }       
1748
1749 /* called with a locked scp and a pointer to a buffer.  Make bulk stat
1750  * calls on all undeleted files in the page of the directory specified.
1751  */
1752 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1753                      cm_req_t *reqp)
1754 {
1755     long code;
1756     cm_bulkStat_t bb;   /* this is *BIG*, probably 12K or so;
1757                          * watch for stack problems */
1758     AFSCBFids fidStruct;
1759     AFSBulkStats statStruct;
1760     cm_conn_t *connp;
1761     AFSCBs callbackStruct;
1762     long filex;
1763     AFSVolSync volSync;
1764     cm_callbackRequest_t cbReq;
1765     long filesThisCall;
1766     long i;
1767     long j;
1768     cm_scache_t *scp;
1769     cm_fid_t tfid;
1770     struct rx_connection * callp;
1771
1772     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
1773
1774     /* should be on a buffer boundary */
1775     osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
1776
1777     bb.counter = 0;
1778     bb.bufOffset = *offsetp;
1779
1780     lock_ReleaseMutex(&dscp->mx);
1781     /* first, assemble the file IDs we need to stat */
1782     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
1783
1784     /* if we failed, bail out early */
1785     if (code && code != CM_ERROR_STOPNOW) {
1786         lock_ObtainMutex(&dscp->mx);
1787         return;
1788     }
1789
1790     /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1791      * make the calls to create the entries.  Handle AFSCBMAX files at a
1792      * time.
1793      */
1794     filex = 0;
1795     while (filex < bb.counter) {
1796         filesThisCall = bb.counter - filex;
1797         if (filesThisCall > AFSCBMAX) 
1798             filesThisCall = AFSCBMAX;
1799
1800         fidStruct.AFSCBFids_len = filesThisCall;
1801         fidStruct.AFSCBFids_val = &bb.fids[filex];
1802         statStruct.AFSBulkStats_len = filesThisCall;
1803         statStruct.AFSBulkStats_val = &bb.stats[filex];
1804         callbackStruct.AFSCBs_len = filesThisCall;
1805         callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1806         cm_StartCallbackGrantingCall(NULL, &cbReq);
1807         osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1808         do {
1809             code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1810             if (code) 
1811                 continue;
1812
1813             callp = cm_GetRxConn(connp);
1814             code = RXAFS_BulkStatus(callp, &fidStruct,
1815                                      &statStruct, &callbackStruct, &volSync);
1816             rx_PutConnection(callp);
1817
1818         } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1819                              &volSync, NULL, &cbReq, code));
1820         code = cm_MapRPCError(code, reqp);
1821
1822         if (code)
1823             osi_Log1(afsd_logp, "CALL BulkStatus FAILURE code 0x%x", code);
1824         else
1825             osi_Log0(afsd_logp, "CALL BulkStatus SUCCESS");
1826
1827         /* may as well quit on an error, since we're not going to do
1828          * much better on the next immediate call, either.
1829          */
1830         if (code) {
1831             cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1832             break;
1833         }
1834
1835         /* otherwise, we should do the merges */
1836         for (i = 0; i<filesThisCall; i++) {
1837             j = filex + i;
1838             tfid.cell = dscp->fid.cell;
1839             tfid.volume = bb.fids[j].Volume;
1840             tfid.vnode = bb.fids[j].Vnode;
1841             tfid.unique = bb.fids[j].Unique;
1842             code = cm_GetSCache(&tfid, &scp, userp, reqp);
1843             if (code != 0) 
1844                 continue;
1845
1846             /* otherwise, if this entry has no callback info, 
1847              * merge in this.
1848              */
1849             lock_ObtainMutex(&scp->mx);
1850             /* now, we have to be extra paranoid on merging in this
1851              * information, since we didn't use cm_SyncOp before
1852              * starting the fetch to make sure that no bad races
1853              * were occurring.  Specifically, we need to make sure
1854              * we don't obliterate any newer information in the
1855              * vnode than have here.
1856              *
1857              * Right now, be pretty conservative: if there's a
1858              * callback or a pending call, skip it.
1859              */
1860             if (scp->cbServerp == NULL
1861                  && !(scp->flags &
1862                        (CM_SCACHEFLAG_FETCHING
1863                          | CM_SCACHEFLAG_STORING
1864                          | CM_SCACHEFLAG_SIZESTORING))) {
1865                 cm_EndCallbackGrantingCall(scp, &cbReq,
1866                                             &bb.callbacks[j],
1867                                             CM_CALLBACK_MAINTAINCOUNT);
1868                 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1869                                 userp, 0);
1870             }       
1871             lock_ReleaseMutex(&scp->mx);
1872             cm_ReleaseSCache(scp);
1873         } /* all files in the response */
1874         /* now tell it to drop the count,
1875          * after doing the vnode processing above */
1876         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1877
1878         filex += filesThisCall;
1879     }   /* while there are still more files to process */
1880     lock_ObtainMutex(&dscp->mx);
1881     osi_Log0(afsd_logp, "END cm_TryBulkStat");
1882 }       
1883
1884 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1885 {
1886     long mask;
1887
1888     /* initialize store back mask as inexpensive local variable */
1889     mask = 0;
1890     memset(statusp, 0, sizeof(AFSStoreStatus));
1891
1892     /* copy out queued info from scache first, if scp passed in */
1893     if (scp) {
1894         if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1895             statusp->ClientModTime = scp->clientModTime;
1896             mask |= AFS_SETMODTIME;
1897             scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1898         }
1899     }
1900
1901     if (attrp) {
1902         /* now add in our locally generated request */
1903         if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1904             statusp->ClientModTime = attrp->clientModTime;
1905             mask |= AFS_SETMODTIME;
1906         }
1907         if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1908             statusp->UnixModeBits = attrp->unixModeBits;
1909             mask |= AFS_SETMODE;
1910         }
1911         if (attrp->mask & CM_ATTRMASK_OWNER) {
1912             statusp->Owner = attrp->owner;
1913             mask |= AFS_SETOWNER;
1914         }
1915         if (attrp->mask & CM_ATTRMASK_GROUP) {
1916             statusp->Group = attrp->group;
1917             mask |= AFS_SETGROUP;
1918         }
1919     }
1920     statusp->Mask = mask;
1921 }       
1922
1923 /* set the file size, and make sure that all relevant buffers have been
1924  * truncated.  Ensure that any partially truncated buffers have been zeroed
1925  * to the end of the buffer.
1926  */
1927 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
1928                    cm_req_t *reqp)
1929 {
1930     long code;
1931     int shrinking;
1932
1933     /* start by locking out buffer creation */
1934     lock_ObtainWrite(&scp->bufCreateLock);
1935
1936     /* verify that this is a file, not a dir or a symlink */
1937     lock_ObtainMutex(&scp->mx);
1938     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
1939                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1940     if (code) 
1941         goto done;
1942         
1943     if (scp->fileType != CM_SCACHETYPE_FILE) {
1944         code = CM_ERROR_ISDIR;
1945         goto done;
1946     }
1947
1948   startover:
1949     if (LargeIntegerLessThan(*sizep, scp->length))
1950         shrinking = 1;
1951     else
1952         shrinking = 0;
1953
1954     lock_ReleaseMutex(&scp->mx);
1955
1956     /* can't hold scp->mx lock here, since we may wait for a storeback to
1957      * finish if the buffer package is cleaning a buffer by storing it to
1958      * the server.
1959      */
1960     if (shrinking)
1961         buf_Truncate(scp, userp, reqp, sizep);
1962
1963     /* now ensure that file length is short enough, and update truncPos */
1964     lock_ObtainMutex(&scp->mx);
1965
1966     /* make sure we have a callback (so we have the right value for the
1967      * length), and wait for it to be safe to do a truncate.
1968      */
1969     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
1970                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
1971                       | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
1972     if (code) 
1973         goto done;
1974
1975     if (LargeIntegerLessThan(*sizep, scp->length)) {
1976         /* a real truncation.  If truncPos is not set yet, or is bigger
1977          * than where we're truncating the file, set truncPos to this
1978          * new value.
1979          */
1980         if (!shrinking)
1981             goto startover;
1982         if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
1983              || LargeIntegerLessThan(*sizep, scp->length)) {
1984             /* set trunc pos */
1985             scp->truncPos = *sizep;
1986             scp->mask |= CM_SCACHEMASK_TRUNCPOS;
1987         }
1988         /* in either case, the new file size has been changed */
1989         scp->length = *sizep;
1990         scp->mask |= CM_SCACHEMASK_LENGTH;
1991     }
1992     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
1993         /* really extending the file */
1994         scp->length = *sizep;
1995         scp->mask |= CM_SCACHEMASK_LENGTH;
1996     }
1997
1998     /* done successfully */
1999     code = 0;
2000
2001   done:
2002     lock_ReleaseMutex(&scp->mx);
2003     lock_ReleaseWrite(&scp->bufCreateLock);
2004
2005     return code;
2006 }
2007
2008 /* set the file size or other attributes (but not both at once) */
2009 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2010                 cm_req_t *reqp)
2011 {
2012     long code;
2013     int flags;
2014     AFSFetchStatus afsOutStatus;
2015     AFSVolSync volSync;
2016     cm_conn_t *connp;
2017     AFSFid tfid;
2018     AFSStoreStatus afsInStatus;
2019     struct rx_connection * callp;
2020
2021     /* handle file length setting */
2022     if (attrp->mask & CM_ATTRMASK_LENGTH)
2023         return cm_SetLength(scp, &attrp->length, userp, reqp);
2024
2025     flags = CM_SCACHESYNC_STORESTATUS;
2026
2027     lock_ObtainMutex(&scp->mx);
2028     /* otherwise, we have to make an RPC to get the status */
2029     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2030
2031     /* make the attr structure */
2032     cm_StatusFromAttr(&afsInStatus, scp, attrp);
2033
2034     tfid.Volume = scp->fid.volume;
2035     tfid.Vnode = scp->fid.vnode;
2036     tfid.Unique = scp->fid.unique;
2037
2038     lock_ReleaseMutex(&scp->mx);
2039     if (code) 
2040         return code;
2041
2042     /* now make the RPC */
2043     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%x", (long) scp);
2044     do {
2045         code = cm_Conn(&scp->fid, userp, reqp, &connp);
2046         if (code) 
2047             continue;
2048
2049         callp = cm_GetRxConn(connp);
2050         code = RXAFS_StoreStatus(callp, &tfid,
2051                                   &afsInStatus, &afsOutStatus, &volSync);
2052         rx_PutConnection(callp);
2053
2054     } while (cm_Analyze(connp, userp, reqp,
2055                          &scp->fid, &volSync, NULL, NULL, code));
2056     code = cm_MapRPCError(code, reqp);
2057
2058     if (code)
2059         osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2060     else
2061         osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2062
2063     lock_ObtainMutex(&scp->mx);
2064     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2065     if (code == 0)
2066         cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
2067                         CM_MERGEFLAG_FORCE);
2068         
2069     /* if we're changing the mode bits, discard the ACL cache, 
2070      * since we changed the mode bits.
2071      */
2072     if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
2073     lock_ReleaseMutex(&scp->mx);
2074     return code;
2075 }       
2076
2077 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2078                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2079 {       
2080     cm_conn_t *connp;
2081     long code;
2082     AFSFid dirAFSFid;
2083     cm_callbackRequest_t cbReq;
2084     AFSFid newAFSFid;
2085     cm_fid_t newFid;
2086     cm_scache_t *scp;
2087     int didEnd;
2088     AFSStoreStatus inStatus;
2089     AFSFetchStatus updatedDirStatus;
2090     AFSFetchStatus newFileStatus;
2091     AFSCallBack newFileCallback;
2092     AFSVolSync volSync;
2093     struct rx_connection * callp;
2094
2095     /* can't create names with @sys in them; must expand it manually first.
2096      * return "invalid request" if they try.
2097      */
2098     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2099         return CM_ERROR_ATSYS;
2100     }
2101
2102     /* before starting the RPC, mark that we're changing the file data, so
2103      * that someone who does a chmod will know to wait until our call
2104      * completes.
2105      */
2106     lock_ObtainMutex(&dscp->mx);
2107     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2108     if (code == 0) {
2109         cm_StartCallbackGrantingCall(NULL, &cbReq);
2110     }
2111     lock_ReleaseMutex(&dscp->mx);
2112     if (code) {
2113         return code;
2114     }
2115     didEnd = 0;
2116
2117     cm_StatusFromAttr(&inStatus, NULL, attrp);
2118
2119     /* try the RPC now */
2120     osi_Log1(afsd_logp, "CALL CreateFile scp 0x%x", (long) dscp);
2121     do {
2122         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2123         if (code) 
2124             continue;
2125
2126         dirAFSFid.Volume = dscp->fid.volume;
2127         dirAFSFid.Vnode = dscp->fid.vnode;
2128         dirAFSFid.Unique = dscp->fid.unique;
2129
2130         callp = cm_GetRxConn(connp);
2131         code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2132                                  &inStatus, &newAFSFid, &newFileStatus,
2133                                  &updatedDirStatus, &newFileCallback,
2134                                  &volSync);
2135         rx_PutConnection(callp);
2136
2137     } while (cm_Analyze(connp, userp, reqp,
2138                          &dscp->fid, &volSync, NULL, &cbReq, code));
2139     code = cm_MapRPCError(code, reqp);
2140         
2141     if (code)
2142         osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2143     else
2144         osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2145
2146     lock_ObtainMutex(&dscp->mx);
2147     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2148     if (code == 0) {
2149         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2150     }
2151     lock_ReleaseMutex(&dscp->mx);
2152
2153     /* now try to create the file's entry, too, but be careful to 
2154      * make sure that we don't merge in old info.  Since we weren't locking
2155      * out any requests during the file's creation, we may have pretty old
2156      * info.
2157      */
2158     if (code == 0) {
2159         newFid.cell = dscp->fid.cell;
2160         newFid.volume = dscp->fid.volume;
2161         newFid.vnode = newAFSFid.Vnode;
2162         newFid.unique = newAFSFid.Unique;
2163         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2164         if (code == 0) {
2165             lock_ObtainMutex(&scp->mx);
2166             if (!cm_HaveCallback(scp)) {
2167                 cm_MergeStatus(scp, &newFileStatus, &volSync,
2168                                 userp, 0);
2169                 cm_EndCallbackGrantingCall(scp, &cbReq,
2170                                             &newFileCallback, 0);
2171                 didEnd = 1;     
2172             }       
2173             lock_ReleaseMutex(&scp->mx);
2174             *scpp = scp;
2175         }
2176     }
2177
2178     /* make sure we end things properly */
2179     if (!didEnd)
2180         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2181
2182     return code;
2183 }       
2184
2185 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2186 {
2187     long code;
2188
2189     lock_ObtainWrite(&scp->bufCreateLock);
2190     code = buf_CleanVnode(scp, userp, reqp);
2191     lock_ReleaseWrite(&scp->bufCreateLock);
2192     if (code == 0) {
2193         lock_ObtainMutex(&scp->mx);
2194         scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
2195                          | CM_SCACHEFLAG_OUTOFSPACE);
2196         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2197                           | CM_SCACHEMASK_CLIENTMODTIME
2198                           | CM_SCACHEMASK_LENGTH))
2199             code = cm_StoreMini(scp, userp, reqp);
2200         lock_ReleaseMutex(&scp->mx);
2201     }
2202     return code;
2203 }
2204
2205 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2206                  cm_user_t *userp, cm_req_t *reqp)
2207 {
2208     cm_conn_t *connp;
2209     long code;
2210     AFSFid dirAFSFid;
2211     cm_callbackRequest_t cbReq;
2212     AFSFid newAFSFid;
2213     cm_fid_t newFid;
2214     cm_scache_t *scp;
2215     int didEnd;
2216     AFSStoreStatus inStatus;
2217     AFSFetchStatus updatedDirStatus;
2218     AFSFetchStatus newDirStatus;
2219     AFSCallBack newDirCallback;
2220     AFSVolSync volSync;
2221     struct rx_connection * callp;
2222
2223     /* can't create names with @sys in them; must expand it manually first.
2224      * return "invalid request" if they try.
2225      */
2226     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2227         return CM_ERROR_ATSYS;
2228     }
2229
2230     /* before starting the RPC, mark that we're changing the directory
2231      * data, so that someone who does a chmod on the dir will wait until
2232      * our call completes.
2233      */
2234     lock_ObtainMutex(&dscp->mx);
2235     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2236     if (code == 0) {
2237         cm_StartCallbackGrantingCall(NULL, &cbReq);
2238     }
2239     lock_ReleaseMutex(&dscp->mx);
2240     if (code) {
2241         return code;
2242     }
2243     didEnd = 0;
2244
2245     cm_StatusFromAttr(&inStatus, NULL, attrp);
2246
2247     /* try the RPC now */
2248     osi_Log1(afsd_logp, "CALL MakeDir scp 0x%x", (long) dscp);
2249     do {
2250         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2251         if (code) 
2252             continue;
2253
2254         dirAFSFid.Volume = dscp->fid.volume;
2255         dirAFSFid.Vnode = dscp->fid.vnode;
2256         dirAFSFid.Unique = dscp->fid.unique;
2257
2258         callp = cm_GetRxConn(connp);
2259         code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2260                               &inStatus, &newAFSFid, &newDirStatus,
2261                               &updatedDirStatus, &newDirCallback,
2262                               &volSync);
2263         rx_PutConnection(callp);
2264
2265     } while (cm_Analyze(connp, userp, reqp,
2266                          &dscp->fid, &volSync, NULL, &cbReq, code));
2267     code = cm_MapRPCError(code, reqp);
2268         
2269     if (code)
2270         osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2271     else
2272         osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2273
2274     lock_ObtainMutex(&dscp->mx);
2275     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2276     if (code == 0) {
2277         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2278     }
2279     lock_ReleaseMutex(&dscp->mx);
2280
2281     /* now try to create the new dir's entry, too, but be careful to 
2282      * make sure that we don't merge in old info.  Since we weren't locking
2283      * out any requests during the file's creation, we may have pretty old
2284      * info.
2285      */
2286     if (code == 0) {
2287         newFid.cell = dscp->fid.cell;
2288         newFid.volume = dscp->fid.volume;
2289         newFid.vnode = newAFSFid.Vnode;
2290         newFid.unique = newAFSFid.Unique;
2291         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2292         if (code == 0) {
2293             lock_ObtainMutex(&scp->mx);
2294             if (!cm_HaveCallback(scp)) {
2295                 cm_MergeStatus(scp, &newDirStatus, &volSync,
2296                                 userp, 0);
2297                 cm_EndCallbackGrantingCall(scp, &cbReq,
2298                                             &newDirCallback, 0);
2299                 didEnd = 1;             
2300             }
2301             lock_ReleaseMutex(&scp->mx);
2302             cm_ReleaseSCache(scp);
2303         }
2304     }
2305
2306     /* make sure we end things properly */
2307     if (!didEnd)
2308         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2309
2310     /* and return error code */
2311     return code;
2312 }       
2313
2314 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2315              cm_user_t *userp, cm_req_t *reqp)
2316 {
2317     cm_conn_t *connp;
2318     long code = 0;
2319     AFSFid dirAFSFid;
2320     AFSFid existingAFSFid;
2321     AFSFetchStatus updatedDirStatus;
2322     AFSFetchStatus newLinkStatus;
2323     AFSVolSync volSync;
2324     struct rx_connection * callp;
2325
2326     if (dscp->fid.cell != sscp->fid.cell ||
2327         dscp->fid.volume != sscp->fid.volume) {
2328         return CM_ERROR_CROSSDEVLINK;
2329     }
2330
2331     lock_ObtainMutex(&dscp->mx);
2332     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2333     lock_ReleaseMutex(&dscp->mx);
2334
2335     if (code)
2336         return code;
2337
2338     /* try the RPC now */
2339     osi_Log1(afsd_logp, "CALL Link scp 0x%x", (long) dscp);
2340     do {
2341         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2342         if (code) continue;
2343
2344         dirAFSFid.Volume = dscp->fid.volume;
2345         dirAFSFid.Vnode = dscp->fid.vnode;
2346         dirAFSFid.Unique = dscp->fid.unique;
2347
2348         existingAFSFid.Volume = sscp->fid.volume;
2349         existingAFSFid.Vnode = sscp->fid.vnode;
2350         existingAFSFid.Unique = sscp->fid.unique;
2351
2352         callp = cm_GetRxConn(connp);
2353         code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2354             &newLinkStatus, &updatedDirStatus, &volSync);
2355         rx_PutConnection(callp);
2356         osi_Log1(smb_logp,"  RXAFS_Link returns 0x%x", code);
2357
2358     } while (cm_Analyze(connp, userp, reqp,
2359         &dscp->fid, &volSync, NULL, NULL, code));
2360
2361     code = cm_MapRPCError(code, reqp);
2362
2363     if (code)
2364         osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2365     else
2366         osi_Log0(afsd_logp, "CALL Link SUCCESS");
2367
2368     lock_ObtainMutex(&dscp->mx);
2369     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2370     if (code == 0) {
2371         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2372     }
2373     lock_ReleaseMutex(&dscp->mx);
2374
2375     return code;
2376 }
2377
2378 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2379                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2380 {
2381     cm_conn_t *connp;
2382     long code;
2383     AFSFid dirAFSFid;
2384     AFSFid newAFSFid;
2385     cm_fid_t newFid;
2386     cm_scache_t *scp;
2387     AFSStoreStatus inStatus;
2388     AFSFetchStatus updatedDirStatus;
2389     AFSFetchStatus newLinkStatus;
2390     AFSVolSync volSync;
2391     struct rx_connection * callp;
2392
2393     /* before starting the RPC, mark that we're changing the directory data,
2394      * so that someone who does a chmod on the dir will wait until our
2395      * call completes.
2396      */
2397     lock_ObtainMutex(&dscp->mx);
2398     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2399     lock_ReleaseMutex(&dscp->mx);
2400     if (code) {
2401         return code;
2402     }
2403
2404     cm_StatusFromAttr(&inStatus, NULL, attrp);
2405
2406     /* try the RPC now */
2407     osi_Log1(afsd_logp, "CALL Symlink scp 0x%x", (long) dscp);
2408     do {
2409         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2410         if (code) 
2411             continue;
2412
2413         dirAFSFid.Volume = dscp->fid.volume;
2414         dirAFSFid.Vnode = dscp->fid.vnode;
2415         dirAFSFid.Unique = dscp->fid.unique;
2416
2417         callp = cm_GetRxConn(connp);
2418         code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2419                               &inStatus, &newAFSFid, &newLinkStatus,
2420                               &updatedDirStatus, &volSync);
2421         rx_PutConnection(callp);
2422
2423     } while (cm_Analyze(connp, userp, reqp,
2424                          &dscp->fid, &volSync, NULL, NULL, code));
2425     code = cm_MapRPCError(code, reqp);
2426         
2427     if (code)
2428         osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
2429     else
2430         osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
2431
2432     lock_ObtainMutex(&dscp->mx);
2433     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2434     if (code == 0) {
2435         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2436     }
2437     lock_ReleaseMutex(&dscp->mx);
2438
2439     /* now try to create the new dir's entry, too, but be careful to 
2440      * make sure that we don't merge in old info.  Since we weren't locking
2441      * out any requests during the file's creation, we may have pretty old
2442      * info.
2443      */
2444     if (code == 0) {
2445         newFid.cell = dscp->fid.cell;
2446         newFid.volume = dscp->fid.volume;
2447         newFid.vnode = newAFSFid.Vnode;
2448         newFid.unique = newAFSFid.Unique;
2449         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2450         if (code == 0) {
2451             lock_ObtainMutex(&scp->mx);
2452             if (!cm_HaveCallback(scp)) {
2453                 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2454                                 userp, 0);
2455             }       
2456             lock_ReleaseMutex(&scp->mx);
2457             cm_ReleaseSCache(scp);
2458         }
2459     }
2460         
2461     /* and return error code */
2462     return code;
2463 }
2464
2465 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2466                    cm_req_t *reqp)
2467 {
2468     cm_conn_t *connp;
2469     long code;
2470     AFSFid dirAFSFid;
2471     int didEnd;
2472     AFSFetchStatus updatedDirStatus;
2473     AFSVolSync volSync;
2474     struct rx_connection * callp;
2475
2476     /* before starting the RPC, mark that we're changing the directory data,
2477      * so that someone who does a chmod on the dir will wait until our
2478      * call completes.
2479      */
2480     lock_ObtainMutex(&dscp->mx);
2481     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2482     lock_ReleaseMutex(&dscp->mx);
2483     if (code) {
2484         return code;
2485     }
2486     didEnd = 0;
2487
2488     /* try the RPC now */
2489     osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%x", (long) dscp);
2490     do {
2491         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2492         if (code) 
2493             continue;
2494
2495         dirAFSFid.Volume = dscp->fid.volume;
2496         dirAFSFid.Vnode = dscp->fid.vnode;
2497         dirAFSFid.Unique = dscp->fid.unique;
2498
2499         callp = cm_GetRxConn(connp);
2500         code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2501                                 &updatedDirStatus, &volSync);
2502         rx_PutConnection(callp);
2503
2504     } while (cm_Analyze(connp, userp, reqp,
2505                          &dscp->fid, &volSync, NULL, NULL, code));
2506     code = cm_MapRPCErrorRmdir(code, reqp);
2507
2508     if (code)
2509         osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
2510     else
2511         osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
2512
2513     lock_ObtainMutex(&dscp->mx);
2514     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2515     if (code == 0) {
2516         cm_dnlcRemove(dscp, namep); 
2517         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2518     }
2519     lock_ReleaseMutex(&dscp->mx);
2520
2521     /* and return error code */
2522     return code;
2523 }
2524
2525 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2526 {
2527     /* grab mutex on contents */
2528     lock_ObtainMutex(&scp->mx);
2529
2530     /* reset the prefetch info */
2531     scp->prefetch.base.LowPart = 0;             /* base */
2532     scp->prefetch.base.HighPart = 0;
2533     scp->prefetch.end.LowPart = 0;              /* and end */
2534     scp->prefetch.end.HighPart = 0;
2535
2536     /* release mutex on contents */
2537     lock_ReleaseMutex(&scp->mx);
2538
2539     /* we're done */
2540     return 0;
2541 }       
2542
2543 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2544                 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2545 {
2546     cm_conn_t *connp;
2547     long code;
2548     AFSFid oldDirAFSFid;
2549     AFSFid newDirAFSFid;
2550     int didEnd;
2551     AFSFetchStatus updatedOldDirStatus;
2552     AFSFetchStatus updatedNewDirStatus;
2553     AFSVolSync volSync;
2554     int oneDir;
2555     struct rx_connection * callp;
2556
2557     /* before starting the RPC, mark that we're changing the directory data,
2558      * so that someone who does a chmod on the dir will wait until our call
2559      * completes.  We do this in vnode order so that we don't deadlock,
2560      * which makes the code a little verbose.
2561      */
2562     if (oldDscp == newDscp) {
2563         /* check for identical names */
2564         if (strcmp(oldNamep, newNamep) == 0)
2565             return CM_ERROR_RENAME_IDENTICAL;
2566
2567         oneDir = 1;
2568         lock_ObtainMutex(&oldDscp->mx);
2569         cm_dnlcRemove(oldDscp, oldNamep);
2570         cm_dnlcRemove(oldDscp, newNamep);
2571         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2572                           CM_SCACHESYNC_STOREDATA);
2573         lock_ReleaseMutex(&oldDscp->mx);
2574     }
2575     else {
2576         /* two distinct dir vnodes */
2577         oneDir = 0;
2578         if (oldDscp->fid.cell != newDscp->fid.cell ||
2579              oldDscp->fid.volume != newDscp->fid.volume)
2580             return CM_ERROR_CROSSDEVLINK;
2581
2582         /* shouldn't happen that we have distinct vnodes for two
2583          * different files, but could due to deliberate attack, or
2584          * stale info.  Avoid deadlocks and quit now.
2585          */
2586         if (oldDscp->fid.vnode == newDscp->fid.vnode)
2587             return CM_ERROR_CROSSDEVLINK;
2588
2589         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2590             lock_ObtainMutex(&oldDscp->mx);
2591             cm_dnlcRemove(oldDscp, oldNamep);
2592             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2593                               CM_SCACHESYNC_STOREDATA);
2594             lock_ReleaseMutex(&oldDscp->mx);
2595             if (code == 0) {
2596                 lock_ObtainMutex(&newDscp->mx);
2597                 cm_dnlcRemove(newDscp, newNamep);
2598                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2599                                   CM_SCACHESYNC_STOREDATA);
2600                 lock_ReleaseMutex(&newDscp->mx);
2601                 if (code) {
2602                     /* cleanup first one */
2603                     lock_ObtainMutex(&newDscp->mx);
2604                     cm_SyncOpDone(oldDscp, NULL,
2605                                    CM_SCACHESYNC_STOREDATA);
2606                     lock_ReleaseMutex(&oldDscp->mx);
2607                 }       
2608             }
2609         }
2610         else {
2611             /* lock the new vnode entry first */
2612             lock_ObtainMutex(&newDscp->mx);
2613             cm_dnlcRemove(newDscp, newNamep);
2614             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2615                               CM_SCACHESYNC_STOREDATA);
2616             lock_ReleaseMutex(&newDscp->mx);
2617             if (code == 0) {
2618                 lock_ObtainMutex(&oldDscp->mx);
2619                 cm_dnlcRemove(oldDscp, oldNamep);
2620                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2621                                   CM_SCACHESYNC_STOREDATA);
2622                 lock_ReleaseMutex(&oldDscp->mx);
2623                 if (code) {
2624                     /* cleanup first one */
2625                     lock_ObtainMutex(&newDscp->mx);
2626                     cm_SyncOpDone(newDscp, NULL,
2627                                    CM_SCACHESYNC_STOREDATA);
2628                     lock_ReleaseMutex(&newDscp->mx);
2629                 }       
2630             }
2631         }
2632     }   /* two distinct vnodes */
2633
2634     if (code) {
2635         return code;
2636     }
2637     didEnd = 0;
2638
2639     /* try the RPC now */
2640     osi_Log2(afsd_logp, "CALL Rename old scp 0x%x new scp 0x%x", 
2641               (long) oldDscp, (long) newDscp);
2642     do {
2643         code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2644         if (code) 
2645             continue;
2646
2647         oldDirAFSFid.Volume = oldDscp->fid.volume;
2648         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2649         oldDirAFSFid.Unique = oldDscp->fid.unique;
2650         newDirAFSFid.Volume = newDscp->fid.volume;
2651         newDirAFSFid.Vnode = newDscp->fid.vnode;
2652         newDirAFSFid.Unique = newDscp->fid.unique;
2653
2654         callp = cm_GetRxConn(connp);
2655         code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2656                              &newDirAFSFid, newNamep,
2657                              &updatedOldDirStatus, &updatedNewDirStatus,
2658                              &volSync);
2659         rx_PutConnection(callp);
2660
2661     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2662                          &volSync, NULL, NULL, code));
2663     code = cm_MapRPCError(code, reqp);
2664         
2665     if (code)
2666         osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2667     else
2668         osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2669
2670     /* update the individual stat cache entries for the directories */
2671     lock_ObtainMutex(&oldDscp->mx);
2672     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2673     if (code == 0) {
2674         cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2675                         userp, 0);
2676     }
2677     lock_ReleaseMutex(&oldDscp->mx);
2678
2679     /* and update it for the new one, too, if necessary */
2680     if (!oneDir) {
2681         lock_ObtainMutex(&newDscp->mx);
2682         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2683         if (code == 0) {
2684             cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2685                             userp, 0);
2686         }
2687         lock_ReleaseMutex(&newDscp->mx);
2688     }
2689
2690     /* and return error code */
2691     return code;
2692 }
2693
2694 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2695               LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2696               u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2697               void **lockpp)
2698 {
2699     long code;
2700     int Which = ((LockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
2701     AFSFid tfid;
2702     AFSVolSync volSync;
2703     cm_conn_t *connp;
2704     cm_file_lock_t *fileLock;
2705     osi_queue_t *q;
2706     int found = 0;
2707     struct rx_connection * callp;
2708
2709     osi_Log1(afsd_logp, "cm_Lock scp 0x%x ...", (long) scp);
2710     osi_Log4(afsd_logp, "cm_Lock type 0x%x offset %d length %d timeout %d",
2711              LockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, Timeout);
2712
2713     /* Look for a conflict.  Also, if we are asking for a shared lock,
2714      * look for another shared lock, so we don't have to do an RPC.
2715      */
2716     q = scp->fileLocks;
2717     while (q) {
2718         fileLock = (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
2719         if ((fileLock->flags & (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING)) == 0) {
2720             if ((LockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
2721                 (fileLock->LockType & LOCKING_ANDX_SHARED_LOCK) == 0)
2722                 return CM_ERROR_WOULDBLOCK;
2723             found = 1;
2724         }
2725         q = osi_QNext(q);
2726     }
2727
2728     osi_Log1(afsd_logp, "cm_Lock found = %d", found);
2729
2730     if (found)
2731         code = 0;
2732     else {
2733         tfid.Volume = scp->fid.volume;
2734         tfid.Vnode = scp->fid.vnode;
2735         tfid.Unique = scp->fid.unique;
2736         lock_ReleaseMutex(&scp->mx);
2737         do {
2738             osi_Log1(afsd_logp, "CALL SetLock scp 0x%x", (long) scp);
2739             code = cm_Conn(&scp->fid, userp, reqp, &connp);
2740             if (code) 
2741                 break;
2742
2743             callp = cm_GetRxConn(connp);
2744             code = RXAFS_SetLock(callp, &tfid, Which,
2745                                   &volSync);
2746             rx_PutConnection(callp);
2747
2748         } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2749                              NULL, NULL, code));
2750         if (code)
2751             osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
2752         else
2753             osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
2754         lock_ObtainMutex(&scp->mx);
2755         code = cm_MapRPCError(code, reqp);
2756     }
2757
2758     if (code == 0 || Timeout != 0) {
2759         fileLock = malloc(sizeof(cm_file_lock_t));
2760         fileLock->LockType = LockType;
2761         cm_HoldUser(userp);
2762         fileLock->userp = userp;
2763         fileLock->fid = scp->fid;
2764         fileLock->LOffset = LOffset;
2765         fileLock->LLength = LLength;
2766         fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2767         osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2768         lock_ObtainWrite(&cm_scacheLock);
2769         osi_QAdd(&cm_allFileLocks, &fileLock->q);
2770         lock_ReleaseWrite(&cm_scacheLock);
2771         if (code != 0) 
2772             *lockpp = fileLock;
2773         osi_Log1(afsd_logp, "cm_Lock Lock added 0x%x", (long) fileLock);
2774     }
2775     return code;
2776 }
2777
2778 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2779                 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2780                 cm_user_t *userp, cm_req_t *reqp)
2781 {
2782     long code = 0;
2783     int Which = ((LockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
2784     AFSFid tfid;
2785     AFSVolSync volSync;
2786     cm_conn_t *connp;
2787     cm_file_lock_t *fileLock, *ourLock;
2788     osi_queue_t *q, *qq;
2789     int anotherReader = 0;
2790     int smallLock = 0;
2791     int found = 0;
2792     struct rx_connection * callp;
2793
2794     osi_Log4(afsd_logp, "cm_Unlock scp 0x%x type 0x%x offset %d length %d",
2795              (long) scp, LockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
2796
2797     if (LargeIntegerLessThan(LLength, scp->length))
2798         smallLock = 1;
2799
2800     /* Look for our own lock on the list, so as to remove it.
2801      * Also, determine if we're the last reader; if not, avoid an RPC.
2802      */
2803     q = scp->fileLocks;
2804     while (q) {
2805         fileLock = (cm_file_lock_t *)
2806             ((char *) q - offsetof(cm_file_lock_t, fileq));
2807         if (!found
2808              && fileLock->userp == userp
2809              && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2810              && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2811             found = 1;
2812             ourLock = fileLock;
2813             qq = q;
2814         }
2815         else if (fileLock->LockType & LOCKING_ANDX_SHARED_LOCK)
2816             anotherReader = 1;
2817         q = osi_QNext(q);
2818     }
2819
2820     /* ignore byte ranges */
2821     if (smallLock && !found) {
2822         osi_Log0(afsd_logp, "cm_Unlock lock not found and ignored");
2823         return 0;
2824     }
2825
2826     /* don't try to unlock other people's locks */
2827     if (!found) {
2828         osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
2829         return CM_ERROR_WOULDBLOCK;
2830     }
2831
2832     /* discard lock record */
2833     osi_QRemove(&scp->fileLocks, qq);
2834     /*
2835      * Don't delete it here; let the daemon delete it, to simplify
2836      * the daemon's traversal of the list.
2837      */
2838     lock_ObtainWrite(&cm_scacheLock);
2839     ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2840     cm_ReleaseUser(ourLock->userp);
2841     lock_ReleaseWrite(&cm_scacheLock);
2842
2843     if (!anotherReader) {
2844         tfid.Volume = scp->fid.volume;
2845         tfid.Vnode = scp->fid.vnode;
2846         tfid.Unique = scp->fid.unique;
2847         lock_ReleaseMutex(&scp->mx);
2848         osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%x", (long) scp);
2849         do {
2850             code = cm_Conn(&scp->fid, userp, reqp, &connp);
2851             if (code) 
2852                 break;
2853
2854             callp = cm_GetRxConn(connp);
2855             code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
2856             rx_PutConnection(callp);
2857
2858         } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2859                              NULL, NULL, code));
2860         code = cm_MapRPCError(code, reqp);
2861
2862         if (code)
2863             osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
2864         else
2865             osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
2866
2867         lock_ObtainMutex(&scp->mx);
2868     }
2869
2870     osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
2871     return code;
2872 }
2873
2874 void cm_CheckLocks()
2875 {
2876     osi_queue_t *q, *nq;
2877     cm_file_lock_t *fileLock;
2878     cm_req_t req;
2879     AFSFid tfid;
2880     AFSVolSync volSync;
2881     cm_conn_t *connp;
2882     long code;
2883     struct rx_connection * callp;
2884
2885     cm_InitReq(&req);
2886
2887     lock_ObtainWrite(&cm_scacheLock);
2888     q = cm_allFileLocks;
2889     while (q) {
2890         fileLock = (cm_file_lock_t *) q;
2891         nq = osi_QNext(q);
2892         if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2893             osi_QRemove(&cm_allFileLocks, q);
2894             free(fileLock);
2895         }
2896         else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2897             tfid.Volume = fileLock->fid.volume;
2898             tfid.Vnode = fileLock->fid.vnode;
2899             tfid.Unique = fileLock->fid.unique;
2900             lock_ReleaseWrite(&cm_scacheLock);
2901             osi_Log1(afsd_logp, "CALL ExtendLock lock 0x%x", (long) fileLock);
2902             do {
2903                 code = cm_Conn(&fileLock->fid, fileLock->userp,
2904                                 &req, &connp);
2905                 if (code) 
2906                     break;
2907
2908                 callp = cm_GetRxConn(connp);
2909                 code = RXAFS_ExtendLock(callp, &tfid,
2910                                          &volSync);
2911                 rx_PutConnection(callp);
2912
2913             } while (cm_Analyze(connp, fileLock->userp, &req,
2914                                  &fileLock->fid, &volSync, NULL, NULL,
2915                                  code));
2916             code = cm_MapRPCError(code, &req);
2917             if (code)
2918                 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
2919             else
2920                 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
2921
2922             lock_ObtainWrite(&cm_scacheLock);
2923         }
2924         q = nq;
2925     }
2926     lock_ReleaseWrite(&cm_scacheLock);
2927 }       
2928
2929 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2930 {
2931     long code;
2932     int Which = ((oldFileLock->LockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
2933     cm_scache_t *scp;
2934     AFSFid tfid;
2935     AFSVolSync volSync;
2936     cm_conn_t *connp;
2937     cm_file_lock_t *fileLock;
2938     osi_queue_t *q;
2939     cm_req_t req;
2940     int found = 0;
2941     struct rx_connection * callp;
2942
2943     if (vcp_is_dead) {
2944         code = CM_ERROR_TIMEDOUT;
2945         goto handleCode;
2946     }
2947
2948     cm_InitReq(&req);
2949
2950     /* Look for a conflict.  Also, if we are asking for a shared lock,
2951      * look for another shared lock, so we don't have to do an RPC.
2952      */
2953     code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2954     if (code)
2955         return code;
2956
2957     q = scp->fileLocks;
2958     while (q) {
2959         fileLock = (cm_file_lock_t *)
2960             ((char *) q - offsetof(cm_file_lock_t, fileq));
2961         if ((fileLock->flags &
2962               (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2963              == 0) {
2964             if ((oldFileLock->LockType & LOCKING_ANDX_SHARED_LOCK) == 0
2965                  || (fileLock->LockType & LOCKING_ANDX_SHARED_LOCK) == 0) {
2966                 cm_ReleaseSCache(scp);
2967                 return CM_ERROR_WOULDBLOCK;
2968             }
2969             found = 1;
2970         }
2971         q = osi_QNext(q);
2972     }
2973
2974     if (found)
2975         code = 0;
2976     else {
2977         tfid.Volume = oldFileLock->fid.volume;
2978         tfid.Vnode = oldFileLock->fid.vnode;
2979         tfid.Unique = oldFileLock->fid.unique;
2980         osi_Log1(afsd_logp, "CALL SetLock lock 0x%x", (long) oldFileLock);
2981         do {
2982             code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2983                             &req, &connp);
2984             if (code) 
2985                 break;
2986
2987             callp = cm_GetRxConn(connp);
2988             code = RXAFS_SetLock(callp, &tfid, Which,
2989                                   &volSync);
2990             rx_PutConnection(callp);
2991
2992         } while (cm_Analyze(connp, oldFileLock->userp, &req,
2993                              &oldFileLock->fid, &volSync,
2994                              NULL, NULL, code));
2995         code = cm_MapRPCError(code, &req);
2996
2997         if (code)
2998             osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
2999         else
3000             osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3001     }
3002
3003   handleCode:
3004     if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
3005         lock_ObtainMutex(&scp->mx);
3006         osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
3007         lock_ReleaseMutex(&scp->mx);
3008     }
3009     lock_ObtainWrite(&cm_scacheLock);
3010     if (code == 0)
3011         oldFileLock->flags = 0;
3012     else if (code != CM_ERROR_WOULDBLOCK) {
3013         oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
3014         cm_ReleaseUser(oldFileLock->userp);
3015         oldFileLock->userp = NULL;
3016     }
3017     lock_ReleaseWrite(&cm_scacheLock);
3018
3019     return code;
3020 }